Sistem de agregare a datelor colectate de la senzori [631898]

Universitatea “Politehnica” din București
Facultatea de Electronic ă, Telecomunicații și Tehnologia Informației

Sistem de agregare a datelor colectate de la senzori
Implementarea unei interfețe web care să permită utilizarea sistemului de agregare

Proiect de diplomă

prezentat ca cerință parțială pentru obținerea titlului de
Inginer în domeniul Electronică și Telecomunicații
programul de studii de licență Electronică Aplicată

Conducător i științific i Absolvent
Ș.L.Dr.Ing. Rodica CONSTANT INESCU Gabriel -Nicolae ANDREI
As.Dr.Ing. Alexandru BUTURUGĂ

2018

Copyright © 2018 , ANDREI N. Gabriel -Nicolae
Toate drepturile rezervate

Autorul acordă UPB dreptul de a reprod uce și de a distribui public copii pe hîrtie sau
electronice ale acestei lucrări, în formă integrală sau parțială.

Anexa 5

Declarație de onestitate academică

Prin prezenta declar că lucrarea cu titlul “ Sistem de agregare a datelor colectate de la senzori ”,
prezentată în cadrul Facultății de Electronică, Telecomunicații și Tehnologia Informației a Universității
“Politehnica” din București ca cerință parțială pentru obținerea titlului de Inginer în domeniu l
Electronică și Telecomunicații , programul de studii Electronică Aplicată este scrisă de mine și nu a mai
fost prezentată niciodată la o facultate sau instituție de învățămînt superior din țară sau străinătate.

Declar că toate sursele utilizate, inclusi v cele de pe Internet, sunt indicate în lucrare, ca
referințe bibliografice. Fragmentele de text din alte surse, reproduse exact, chiar și în traducere
proprie din altă limbă, sunt scrise între ghilimele și fac referință la sursă. Reformularea în cuvinte
proprii a textelor scrise de către alți autori face referință la sursă. Înțeleg că plagiatul constituie
infracțiune și se sancționează conform legilor în vigoare.

Declar că toate rezultatele simulărilor, experimentelor și măsurătorilor pe care le prezint c a
fiind făcute de mine, precum și metodele prin care au fost obținute, sunt reale și provin din
respectivele simulări, experimente și măsurători. Înțeleg că falsificarea datelor și rezultatelor
constituie fraudă și se sancționează conform regulamentelor în vigoare.

București,
06.09.2018
Absolvent: [anonimizat] 11
Lista acronimelor …………………………………………………………………………………………….. ………………. .. 13
Introducere ………………………………………………………… ………………………………………….. ……….. …….. … 15
1. Noțiuni teoretice …………………………………………………………………………………………… …………. …… 17
1.1. Lista limbajelor de programare și de descriere utilizate …………………….. …………………….. …. 17
1.2. Componentele electronice din structura nodurilor ……………………………………………. …………. 20
1.3. Procesul decizional de proiectare ………………………………………………………………….. …………. . 23
2. Arhitectura hardware și software a sistemului ……………………………………………. …………. …………. 27
3. Procesul de implementare al sistemului …………………………………………………………. …………. …….. 33
3.1. Implementarea componentei logice ………………………………………………………………. …………. . 33
3.2. Implementarea componentei fizice ……………………………………………………. …….. ………………. 48
Conclu zii ……………………………………………………………………………………………………. …………. …………. 51
Bibliografie …………………………………………………………………………………. …………………. …………. …….. 53
Anexa 1 …………………………………………………………………………………………………………….. …………….. . 57
Anexa 2 ………………………………………. ………………………………………………………………. …………. ……….. 57
Anexa 3 ……………………………………………………………………………………………………………. …………. …… 59
Anexa 4 ……………………………………………………………………………………………………………. …………. …… 67
Anexa 5 ………………………………………………………………………………… …………………………… …………. …. 73
Anexa 6 ………………………………………………………………………………… …………………………… …………. …. 77
Anexa 7 ……………………………………………………………………………….. …………………………… …………. ….. 79

Lista figurilor
Număr Titlu Pagină
Figura 1.1 Raspberry Pi 3 Model B+ 20
Figura 1.2 Raspberry Pi 1 Model B 21
Figura 1.3 Modul senzor de temperatură și umiditate DHT22 21
Figura 1.4 Modul senzor de gas MQ -2 22
Figura 1.5 Convertor Analog -Digital MCP3002 22
Figura 1. 6 Model de extindere pe orizontală (Horizontal Scaling) 23
Figura 1.7 Modelul iterativ de implementare software 23
Figura 1.8 Diagrama arhitecturală a părții logice 24
Figura 2.1 Diagrama simplificată de interacțiune între nodurile worker (muncitor),
baza de date, nod de control și utilizator 25
Figura 2.2 Diagrama părții logice a sistemului de autentificare 27
Figura 2.3 Modelul grafic de proiectare al paginii ”Dispozitive” 28
Figura 2.4 Modelul grafic de proiectare al paginii aferente fiecărui nod
worker(muncitor) 28
Figura 2.5 Schema vizuală a montajului electric 29
Figura 2.6 Schema electrică de legătură a componentelor necesare montajelor
corelată cu indicativele și funcționalitățile pinilor plăcii de dezvoltare 30
Figura 3.1 Schema bazei de da te + tabele asociate 32
Figura 3.2 Exemplu de start pentru o aplicație dezvoltată cu Flask 33
Figura 3.3 Pagina de autentificare 35
Figura 3.4 Pagina de dispozitive 35
Figura 3.5 Element implementat prin utilizarea librăriei externe JustGage 37
Figura 3.6 Selectoarele pentru intervalele de timp + graficele cu ultimele 10
măsurători efectuate, primă parte a paginii fiecărui nod 39
Figura 3.7 Graficele obținute prin intermediul funcționalității de agregare a datelor, a
doua parte din pagina fiecărui nod 40
Figura 3.8 Diagrama logică a generării paginii atașate fiecărui nod worker(muncitor) 41
Figura 3.9 Evidențierea selectoarelor de date și a selectorului pentru intervalul de
agregare al datelor 42
Figura 3.10 Evidențierea formatului final utilizat pentru d ate 44

Lista acronimelor
ADC Analogue -to-Digital Converter Convertor Analog -Digital
ARP Address Resolution Protocol Protocol de Analiză al Adresei
CD Compact Disc Disc Compact
CH0,1 Channel 0,1 Canal 0,1
CRUD Create , Read, Update, Delete Creează, Citește, Actualizează, Șterge
CSS Cascading Style Sheets Foi cu Cascade de Stiluri
DHCP Dynamic Host Configuration Protocol Protocol de configurare dinamică a gazdei
GPIO General -Purpose Input/Output Port cu scop generic de intrare/ieșire
HTML Hypertext Markup Language Limbaj de marcare al Hipertextului
HTTP Hypertext Transport Protocol Protocol de transport al Hipertextului
IoT Internet of Things Internetul Lucrurilor
IP Internet Protocol Protocolul Internet
I2C I-squared -C Port serial sincron I2C
JS JavaScript JavaScript
Mbps Megabites per second Mega biti pe secundă
MHz Mega Hertz Mega Hertz
mmHg Millimeter of Mercury Milimetri Coloană de Mercur
MVC Model -View -Controller Model -Vizualizare -Controler
NTC Negativ e Temperature Coefficient Coeficient de temperatură negativ
SBC Single -Board Computer Calculator pe o Singură Placă
SOC System -on-Chip Sistem pe cip
SPI Serial Peripheral Interface Interfață Serială Periferică
SQL Structured Query Language Limbaj de În trebări Structurate
SSH Secure Shell –
TCP Transmission Control Protocol Protrocol de Control al Transmisiei
UART Universal asynchronous receiver -transmiter Port universal asincron de primire și trimitere
WiFi Wireless Fidelity –

15
Introducere

Obiectivul lucrării
Lucrarea prezintă procesul de proiectare și implementare a l unui sistem ce permite agregarea
datelor colectate de la diferiți senzori. Principalul obiectiv al acestui sistem este de a oferi utilizatorului
capabilitatea de a își alege intervale de timp după preferințe proprii , extrem de personalizabile până la
nivelul de secund ă, capabilitate pe care poate să o utilizeze eficient direct dintr -o interfață web special
dezvoltată și axată doar pe această temă .
În cadrul lucrării se vor utiliza termenii de sistem , care face referire la ansamblul de colectare
al datelor si de control, și termenul de nod care va fi utilizat pentru a realiza descrierea dispozitivelor
ce fac parte din sistem, reprezentate de plăcu țe de dezvoltare de tipul Single -Board Computer (SBC) .
Sistemul propus în lucrare dorește să implementeze în plus, în comparație cu alte sisteme,
această capabilitate descrisă anterior. Sistemul implementat este compus dintr -un set de noduri, trei la
numă r, conectate la o rețea locală și interogate prin intermediul aplicației web. Aplicația deține un
sistem de autentificare pe baza un ui set de perechi compusă din nume utilizator și parola, aducând
astfel un plus la nivelul de securitate.
Sistemul se mai dorește a fi o soluție similară cu cea de IoT (Internet of Things) datorită
modului de comunicare între dispozitive, acesta fiind realizat printr -o rețea de internet locală, și
datorită prezenței unei interfețe web care este accesibilă atât din browserul d e pe un calculator personal,
cât și de pe browserul telefonului mobil, atâta timp cât utilizatorul se află în aceeași rețea.
Sistemul ca un tot este compus dintr -un nod de control și un număr de 2 noduri worker
(muncitoare), dar un aspect important de men ționat este că poate fi ad ăugat un număr nedeterminat de
noduri worker, astfel permițând extinderea ariei de monitorizare. Prin intermediul descrieri menționate
mai sus referitoare la nodurile care fac parte din sistem rezultă faptul că arhitectura de bază pentru
implementarea acestui sistem este una de tip distribuit semi -modulară. Prin arhitectură distribuit semi –
modulară se vizează faptul ca sistemul permite extinderea de noduri worker, dar dacă se pierde nodul
de control sistemul nu mai poate fi utiliz at.

16
Conținutul lucrării
Lucrarea conține din punct de vedere structural mai multe capitole, fiecare dintre aceste
capitole reprezentând o parte importantă în vederea prezentării proiectului, cât și a etapelor de
dezvoltare a întregului sistem.
Primul capitol, ”Noțiuni teoretice” , vizează o introducere teoretică, pe scurt, a tehnologiilor și
a componentelor electronice , reprezentând parte componentă a sistemului, cât și a procesului
decizional de proiectare a acestuia.
Partea de contribuții prac tice va fi descrisă în următoarele capitole prezentate mai jos:
Al doilea capitol, ”Arhitectura hardware și software a sistemului”, conține prezentarea
arhitecturii sistemului, schemele si diagramele ce au fost implementate atât din punct de vedere fizic,
cât și logic.
Al treilea capito l, ”Procesul de implementare al sistemului” va conține prezentarea
implementării propriu zise a sistemului, având la bază cerințele si datele de proiectare. Etapa de
implementare descrisă în acest capitol vizează implementa rea fizică a sistemului, testarea pentru o
bună funcționare a sistemului fizic, urmată de implementarea finală a sistemului de agregare, proces
făcut în paralel cu etapa de testare a acestuia.
În încheierea lucrării vor fi prezentate concluziile trase din urma produsului finit și a modului
de funcționare a acestuia, se va realiza de asemenea o comparație cu alte sisteme care prezintă
funcționalități similare, atât cu precizarea avantajelor și dezavantajelor, discuție încheiată prin
prezentarea ideilor de î mbunătățire ulterioară a sistemului prezentat.

Contribuțiile practice aduse în cadrului proiectului
Proiectarea si dezvoltarea părții logice și fizice a sistemului
1. configurarea nodurilor prin pregătirea sistemului de operare, configurarea bazei de date ș i
implementarea structurii de tabele
2. configurarea procesului de interogarea a senzorilor, de prelucrare a datelor obținute, și de
stocare în baza de date prin dezvoltarea codului aferent
3. implementarea funcționalităților de interogare a bazei de date, prelu crare si agregare a
datelor colectate, urmat de realizarea interfeței grafice pentru o utilizare mai ușoară a
sistemului
4. crearea montajelor specifice fiecărui nod prezent în sistem, prin conectarea componentelor
specifice

17
1. Noțiuni teoretice
În realizare a unui sistem tehnologic care este bazat pe tehnologii informatice, exista foarte
multe variante de tehnologii care pot fi utilizate pentru implementarea acestuia. În cazul de față, mai
exact, în proiectarea sistemului prezentat s -a utilizat o combinație î ntre mai multe limbaje de
programare, cât și limbaje de descriere.
De asemenea pe lângă partea software/logică, este vizată în prezentare si partea fizică, unde
vor fi descrise componentele utilizate în realizarea sistemului, atât ca utilitate, cât și ca s pecificații.

1.1. Lista limbajelor de programare și de descriere utilizate
În realizarea proiectului, majoritar în partea de implementare a funcționalităților se află
limbajul de programare Python, ale cărui funcționalități sunt extinse prin utilizarea de lib rării si
framework -uri, aici putând menționa: Flask, matplotlib, pandas, psycopg2, descrise ulterior mai jos.
Limbajele de descriere utilizate și ale căror rol a fost de a proiecta interfața grafică sunt: HTML
(Hypertext Markup Language), CSS (Cascading St yle Sheets) , JS (JavaScript) , Jinja2 .

Descrierea pe scurt a limbajelor utilizate
Python
Guido van Rossum a creat limbajul de programare Python în anii 1980 și în comparație cu alte
limbaje precum C, C++, Java, Python se remarcă printr -o sintaxă simplă, d ar puternică. Python este
utilizat pentru dezvoltarea software la companii cunoscute la nivel mondial, precum Google, CERN,
NASA etc . Frumusețea limbajului de programare Python este determinat de faptul că este accesibil și
programatorilor începători permi țându -le să rezolve probleme mult mai ușor în comparație cu alte
limbaje care au o curbă de învățare mult mai abruptă. [1]
Un alt avantaj major al limbajului de programare Python , pe lângă o sintaxă simplă și ușor de
învățat, este un limbaj ideal pentru sc ripting și de asemenea pentru dezvoltarea rapidă a aplicațiilor din
foarte multe arii de studiu. De asemenea, prezintă funcționalitatea de a scrie programe mult mai
compacte și inteligibile, în comparație cu alte limbaje (C, C++, Java) din următoarele moti ve[2]:
– permite realizarea de operații complexe într -un singur rând/linie de cod [2]
– gruparea codului se face prin intermediul indentării, în defavoarea utilizării acoladelor [2]
– nu este necesară declararea tipului de dată pentru variabilele utilizare [2]
Librării utilizate prin intermediul limbajului Python
Flask
Flask este un microframework care poate fi utilizat într -o aplicație Python. Avantajul
framework -ului Flask este determinat de faptul că depinde foarte puțin spre deloc de librării
externe. Dezavan tajul este datorat nevoii de a adăuga diverse librării pentru a extinde aria de

18
funcționalitate si capabilitățile acestuia astfel crescând numărul de dependențe asociate
aplicației . [3]
Librăria implicită nu conține capabilități de conectare la o bază de d ate și oferă
utilizatorului oportunitatea de a decide ce tehnologii dorește și care se mulează cel mai bine pe
aplicația dorită. Flask s -a dezvoltat pe premiza de a fi tot ce ai nevoie si nimic din ce nu ai
nevoie . În cazul framework -ului Flask, micro nu î nseamnă ca întreaga aplicație web trebuie să
fie plasată într -un singur fișier (chiar dacă este posibil și acest lucru), ci țintește spre a oferi
utilizatorului un nucleu al aplicației simplu, dar extensibil. [4]

matplotlib
Matplotlib este o librărie de trasare a graficelor 2D și are ca scop simularea
funcționalităților oferite de bine cunoscutul limbaj de programare MATLAB . Matplotlib a fost
dezvoltat având ca filozofie faptul că utilizatorul trebuie să fie capabil să creeze simple
reprezentări de grafic e prin intermediu a câtorva linii de cod sau chiar una singură. Avantajul
librăriei matplotlib este că permite utilizatorului să folosească în diverse medii rezultatele
obținute, precum o aplicație pe calculator, chiar și într -o interfață web. [5]

panda s
Pandas este o librărie Python care asigură lucrul într -o maniera rapidă, flexibilă și
intuitivă cu structuri de date. Țintește spre a oferi funcționalități ușor de utilizat pentru a realiza
procese de analiză si manipulare a datelor. Pandas este foarte p otrivit când vine vorba de lucrul
pe diferite seturi de date care necesită o analiză complexă de aceea este foarte potrivit în
aplicații cu caracter științific. [6]

psycopg2
Psycopg este o librărie care, prin utilizare, permite accesul spre o bază de date
PostgreSQL , direct dintr -o aplicație dezvoltată în Python. Principalele caracteristici sunt faptul
că implementează absolut ce se cere de la o librărie care sa permită accesul spre o bază de date
și de asemenea oferă garanția că poate să suporte un număr mare de conexiunii spre baza de
date inițiate direct de utilizatori direct prin intermediul aplica ției. [7]

HTML (Hypertext Markup Language)
HTML este un limbaj de descriere care definește felul în care este structurată și afișată o pagina
web. HTML permi te afișarea sub diferite formate, de la structurarea prin intermediul paragrafelor,
până la utilizarea elementelor de tip listă, introducerea de imagini sau crearea de tabele. [8]

19
HTML nu trebuie confundat ca fiind un limbaj de programare, el este un limba j de marcare
care structurează conținutul unei pagini. HTML este alcătuit dintr -o serie de elemente (elements)
utilizate pentru a modifica comportamentul de afișare a conținutului. [8]

CSS (Cascading Style Sheets)
CSS este un limbaj de stilizare al eleme ntelor descrise în HTML. CSS permite o gamă largă de
modificări la nivel de pagină web, de la schimbarea culorii literelor/textului sau a culorii de fundal,
până la poziționarea efectivă a elementelor dintr -o pagină web. Codul CSS poate fi folosit direct î n
codul sursă al paginii care se dorește a fi stilizată sau poate fi chemat dintr -un fișier extern, metodă
mult mai recomandată deoarece permite o structurare a fișierelor aplicației într -o manieră mai
eficienta. [9]

JS (JavaScript)
Ca definiție de nivel înalt, JavaScript este un limbaj de programare care permite implementarea
lucrurilor complexe într -o pagină web, spre exemplu hărți sau grafice interactive. Acest lucru este
posibil datorită utilizării limbajului JavaScript în implementarea codului sursă a l paginii. De asemenea,
JavaScript poate fi utilizat ca și limbaj de scripting care permite creearea de conținut dinamic. [10]

PostgreSQL
PostgreSQL este un SGBD (Sistem de gestiune a bazelor de date), care permite realizarea de
diverse operații la nivel ul bazelor de date, precum stocarea datelor, accesarea acestora , păstrarea de
copii în cazul unor viitoare probleme. O bază de date este o colecție de date, care este folosită pentru
diverse scopuri. Pe lângă faptul ca PostgreSQL este un SGBD, acesta funcț ionează de asemenea și ca
server multi -utilizator, permițând mai multor utilizatori să folosească o bază de date simultan. [11]

20
1.2. Componentele electronice din structura nodurilor
Descriere Raspberry Pi este o placă de dezvoltare SBC (Single -Board Computer) – descris ca fiind un
sistem de calcul nemodular implementat pe un singur cablaj electronic. Raspberry Pi țintește spre a
avea dimensiuni reduse (86.9mm x 58.5mm x 19.1mm – varianta Raspberry Pi 3) , dar să ofere în
continuare funcționalități asem ănătoare cu cele oferite de un calculator personal obișnuit, dar cu o
putere de calcul redusă. [12]
▪ Raspberry Pi 3 Model B + este un SBC care are la bază un procesor quad -core ARM Cortex
A53 64 -bit cu o frecvență de 1.2GHz , încapsulat într -un SOC (System -on chip), Boardcom
BCP2837 și 1GB RAM . Pe lângă aceste specificații, mai deține de asemenea o placă grafică
integrată VideoCore IV la 400MHz. Este o placă multifuncțională ce oferă suport pentru
comunicație serială prin intermediul celor 2 interfețe SPI (SPI 0/1), comunicații prin porturi
generice I/O (Input/Output – Intrare/Iesire) (GPIO) sau prin intermediul comunicației seriale
prin interfată UART . Per total, placa de dezvoltare deține un număr impresionant de porturi
pentru a putea oferi utilizatorului un spațiu de lucru decent, reprezentate din 27 porturi generice
I//O (GPIO), UART, I2C, SPI. De asemenea oferă pini suplimentari pentru alimentarea
componentelor electronice atașate la placă cu o tensiune de ieșire de 3.3V si 5V . [11] [12]

Figura 1.1 Raspbe rry Pi 3 Model B+
Sursă [13]
▪ Raspberry Pi 1 Model B este de asemenea tot un SBC din primele generații dezvoltate pentru
această gamă de plăci de dezvoltare. Prezintă un procesor mai slab ca specificații, mai exact un
ARM1176JZF -S care funcționează la o fre cvență de 700M Hz, încapsulat intr -un SOC (System –
on-Chip) Broadcom BCM2835 . Din punct de vedere al capabilităților de comunicare cu alte

21
componente electronice, deține doar o interfață SPI (SPI0) în comparație cu versiunile mai nou .
Modelul deține de aseme nea și un port Ethernet 10/100 Mbps ce permite plăcii să fie conectată
la o rețea pentru a avea access la internet. [12]

Figura 1.2 Raspberry Pi 1 Model B
Sursă [14]
▪ DHT22 este un modul senzor de măsurare a temperaturii și umidității. Măsurătorile se fac prin
intermediul unui NTC (Negative Temperature Coefficient) , pentru parametrul de temperatură,
și de un senzor capacitiv, pentru a obține o umiditate relativă. Ieșirile sunt conectate la un
microcontroller pe 8 biți care scoate la ieșire un semna digital . Intervalele suportate de modulul
DHT22 sunt, pentru temperatură un interval între -40 80 *Celsius, cu o precizie foarte bună de
+/- 0.5 *Celsius, iar pentru umiditate intervalul este de 0 -100%, cu o precizie de +/ – 2%.
Tensiunea de lucru a modulului este cuprinsă în intervalul 3.3V – 5V [15]

Figura 1.3 Modul sen zor de temperature și umiditate DHT22
Sursă [1 5]

22
▪ MQ-2 este un modul senzor ce permite detecția scurgerilor de gaze din încăpere. Nu se
limitează doar la 1 -2 tipuri de gaze cunoscute și prezintă abilitatea de a înregistra modificări în
prezența GPL -ului (Gaz Petrolier Lichefiat), izobutanului, propanului, metanului si fum. Din
punct de vedere tehnic modulul prezintă atât ieșire de date analogică (AO), cât și ieșire de date
digitală (DO), recomanda tă pentru o precizie de lucru mai bună, ieșirea de date analogică.
Funcționează la o tensiune de 5V, și are ca temperatură optimă de lucru intervalul -20 50 *C .
Datorită costului redus, MQ -2 este un senzor perfect ce poate fi ușor integrat într -o gamă larg ă
de aplicații. [16]

Figura 1.4 Modul senzor de gaz MQ -2
Sursă [1 6]
▪ BMP180 este un modul senzor ce permite detecția valorilor presiunii atmosferice, a
temperaturii și a altitudinii. Este un senzor de dimensiuni reduse facându -l astfel potrivit
pentru o g amă largă de aplicații. Funcționează la o tensiune de alimentare cuprinsă între 1.8V
– 3.6V și un curent de 5uA. Intervalul de presiune atmosferică măsurat este de 300 hPa până
la 1100 hPa, tradus in mmHg, intervalul este de 225 până la 825 mmHg (milimetri coloană de
mercur). Prezintă o rezoluție extrem de bună pentru măsurarea valorii presiunii atmosferice,
de 0.03 hPa (hecto pascali). Citirea datelor se face prin intermediul protocolului de
comunicație sincron I2C. [17]

Figura 1.5 Modul senzor de presiu ne atmosferică, altitudine și temperatură BMP180 [17]

23
▪ MCP3002 este un ADC (Analogue -to-Digital Converter – Convertor Analog -Digital) ce are
ca avantaj o performanță ridicată raportată la un consum de putere mic. Modulul are la bază o
arhitectură de calcul realizată printr -un registru de aproximări succesive, controlul fiind realizat
prin intermediul interfeței SPI, permițând astfel ca MCP3002 să fie utilizat cu orice placă ce
deține o interfață compatibilă. Rezoluția pe care o are acest convertor este de 10 biți, cu 2 canale
de intrare (CH0, CH1) unde componentele care dețin doar ieșire analogică pot fi conectate.
Fiind un modul de dimensiuni reduse, cu un consum mic de putere și cu un canal de comunicare
foarte des întâlnit, MCP3002 poate fi ușor utilizat î ntr-o gamă largă de aplicații precum
achiziția de date, interfațarea cu senzori sau controlul unor procese.

Figura 1.5 Convertor Analog -Digital MCP3002
Sursă [18]
1.3. Procesul decizional de proiectare
În procesul de proiectare efectivă a sis temului, din punct de vedere fizic nu putem discuta
despre o tehnică generală de modelare a întregului sistem, fiind foarte ușor comparabilă cu tehnica ”de
extindere pe orizontală ”, în special pentru nodurile al căror rol este de a realiza măsurarea parame trilor
doriți si stocarea acestora.
Conform diagramei prezentate mai jos, în Figura 1.6, se poate observa faptul ca utilizatorul
poate să integreze în sistem orice număr de noduri de lucru (Workers) și nu o să afecteze
funcționalitatea sistemului ca un înt reg.

Figura 1.6 Model de extindere pe orizontală (Horizontal Scaling)

24

Din punct de vedere software în procesul de proiectare s -a folosit modelul iterativ care vizează
un set de 5 (cinci) etape de lucru, mai exact: analizarea cerințelor, proiectarea sist emului,
implementare, testare, instalar e; etape care pot fi reluate în orice moment până la momentul finalizării
sistemului. S -a decis să se folosească acest model deoarece proiectul a avut atât parte fizică de
proiectare, cât și logică, etape ale cărui mo del se potriveau perfect in procesul de proiectare.

Figura 1.7 Mo delul iterativ de implementare software
Sursa [1 9]
Din punct de vedere arhitectural al părții logice nu s -a folosit un model standard, precum
modelul MVC (Model -View -Controller), ci s -a dor it o implementare proprie relativ similară cu cea
menționată anterior , formată din module de analiză a datelor separate, dar care interacționează între
ele în funcție de caz.

Figura 1.8 Diagrama arhitecturală a părții logice
Din diagrama prezentată mai sus se poate observa faptul ca partea logică nu interacționează
efectiv cu nodurile care măsoară temperatura, umiditatea si nivelul de gaz din încăpere, ci cu o bază
de date, unde nodurile menționate anterior, inserează acolo datele. Interfața web este rea lizată pe baza
a ceea ce se numește View (pagină web) și este accesată de utilizator folosind Routes (routele)
implementate în backend. Accesul la date se face prin intermediul request -urilor (cererilor) de tip GET
și POST, aceste cereri fiind primite de b ackend, pe baza cărora apelează logica de prelucrare a datelor,

25
care este izolată, ceea ce se obține fiind doar rezultatul final al prelucrării. Logica de prelucrare a
datelor constă într -un set de funcții care interacționează mai mult sau mai puțin între ele, după caz, și
care au rol sa primească parametrii ceruți de utilizator, pe baza cărora va face interogarea bazei de date
urmând sa prelucreze datele obținute.
În partea fizică de realizare efectivă a montajului care să permită citirea datelor s -au util izat plăci de
dezvoltare Raspberry Pi (precum cele descrise mai sus) care permit lucrul cu un sistem de operare,
deci astfel s -a putut utiliza limbajul de programare Python pentru citirea datelor de la componentele
electronice atașate plăcilor menționate m ai sus.
Pentru a realiza o implementare eficientă și rapidă s -a decis utilizarea de librării cu funcții de
citire deja dezvoltate și configurate pentru componentele utilizate în cadrul montajului.
Partea fizică a implicat de asemenea și configurarea unei rețele locale în care să fie plasate cele
3 noduri, pentru a facilita comunicarea între ele. Pentru acest lucru s -a decis utilizarea unui router
(dispozitiv de rutare in rețea) simplu, care să aibă capabilitatea de a oferi un canal de comunicație
wireless astfel încât dispozitivele să se poată lega la rețea, prin configurarea de adrese IP configurate
static, și de a iniția procesul de comunicare.

26

27
2. Arhitectura hardware și software a sistemului
De la sistem s -a dorit să se monitorizeze, într -o încăpere, urmă torii parametrii:
▪ temperatura interioară ;
▪ nivelul de umiditate din încăpere ;
▪ nivelul de gaz, reprezentat în special de cel propan și butan, fiind ușor detectate de senzorul
aferent .
Din punct de vedere al funcționalității oferite utilizatorului s-au dorit următoarel e:
▪ controlul asupra vizualizării unui interval personalizat al datelor ;
▪ agregarea datelor vizualizate sub un anumit interval de timp ;
▪ vizualizarea în timp real al datelor și de asemenea a ultimelor 10 înregistrări, funcții implicite
în aplicație .
Mai jos este prezent ă și o schemă puțin mai simplificată a întregului sistem și cum se realizează
comunicarea între utilizator -nod de control și între nod -control -bază de date -noduri
worker(lucrătoare).

Figura 2.1 Diagrama simplificată de interacțiune î ntre noduri lucrătoare, bază de date, nod de control
și utilizator

Plăcile de dezvoltare care au ca scop atribuit de a prelua datele de la senzori, a prelucra aceste
date și de a le trimite prin intermediul rețelei locale în baza de date, vor fi realizate prin implementarea
unui aplicații separate de aplicația principală, care va fi găzduită pe plăcile menționate mai sus. La fel
cum și aplicația principală este dezvoltată în Python, s -a decis reutilizarea acestuia în cadrul
implementării acestei funcțional ități.

28
Din figura 2.1 mai reiese faptul că orice utilizator care se află în aceeași rețea ca și sistemul
prezentat, poate să acceseze prin intermediul unui web browser interfața grafică, dezvoltată strict
pentru a eficientiza procesul de cerere al datelor sub un anumit format.
De menționat în continuare este faptul că nodul de control, pe lângă găzduirea aplicației propriu
zise, găzduiește, de asemenea, și server de baze de date. Acest aspect al prezenței bazei de date pe
nodul de control se putea elimina p rin utilizarea unei baze de date găzduită într -un sistem de tip cloud
(Google Cloud), însă în vederea prezentări s -a presupus că nu va exista conexiune activă la internet, ci
doar la rețeaua locala.
Sistemul, ca un întreg presupune si niște etape de confi gurare minime, astfel încât acesta să permită
rularea aplicației principale, de prelucrare a datelor cerute de utilizator si oferire a răspunsului sub
forma unor grafice sau elemente care se actualizează cu ultimele date, aplicației secundare de citire a
parametrilor măsurați de senzori și de rulare a serviciului server de bază de date , astfel :
1. Configurarea plăcilor de dezvoltare prin:
1.1. instalarea unui sistem de operare bazat pe Linux, în cazul de față s -a utilizat o versiune
cu funcționalități minime pentru a nu suprasolicita resursele hardware (rasbian stretch lite) ;
1.2. configurarea accesului prin SSH (Secure Shell) și configurarea interfeței de conectare
la rețeaua locală prin WiFi ;
1.3. realizarea montajelor aferente, prin conectarea senzorilor la nodurile worker și
verificarea acestor montaje prin teste simple .
2. Configurarea mediului de lucru prin :
2.1. actualizarea pachetelor curente și a locațiilor de unde pot fi ulterior instalate noi pachete ;
2.2. pregătirea mediului unde va rula aplicația, prin instalarea pachetelor de care depind atât
aplicația principală aflată pe nodul de control, cât și aplicația secundară aflată pe nodurile
worker .
Tot acest proces descris mai sus nu a fost realizat într -o singură etapă, și a fost dedus în timpul
dezvoltării , prin intermediul metod ei de lucru iterative.

Aplicația principală, cu care utilizatorul interacționează, deține prin implementare și un sistem
de autentificare pe baza unei perechi de forma nume utilizator & parolă utilizator . O simplă descriere
a modului în care este impleme ntată aceasta funcționalitate este reprezentată de utilizarea unei sesiuni,
care rămâne activă cât timp utilizatorul rămâne pe pagina de interfață grafică. Mai jos este descris tot
acest proces de autentificare și de verificare al sesiunii curente printr -o diagrama .

29

Figura 2.2 Diagrama părții logice a sistemului de autentificare
O descriere mai amplă a modului în care acest proces se execută va fi discutat în capitolul 3,
subcapitolul 1, care descrie în detaliu mecanismul de execuție al acestui proces rea lizat la un nivel
macro, oferind astfel doar o variantă simplă și optimă fără prea multe elemente complexe.
Interfa ța grafică creată pentru a oferi utilizatorului o experiență de lucru cu aplicația a țintit în
a avea 2 mari secțiuni, după cum urmează :
• O secțiune, reprezentată printr -o pagină web, care să ofere informații în timp real despre
valorile curente ale parametrilor de temperatură, umiditate , presiune atmosferică și
nivel de gaz și a nivelului de disponibilitate a nodurilor worker, care contribuie l a
trimiterea parametrilor menționați mai sus spre baza de date.
• O secțiune, reprezentată dintr -un număr de pagini web determinat de numărul de noduri
worker, care vizează prezentarea ultimelor înregistrări efectuate, a parametrilor
menționați mai sus, și d e asemenea de a realiza obiectivul principal al sistemului, de a
permite agregarea particularizată a datelor înregistrate.
În diagramele următoare este prezentată structura acestor 2 secțiuni, detalierea din punct de
vedere logic, făcându -se în capitolul 3 .

30

Figura 2.3 Modelul grafic de proiectare al paginii ”Dispozitive”

Figura 2.4 Modelul grafic de proiectare al paginii aferente fiecărui nod worker

31
În cadrul sistemului sunt prezente 2 noduri worker(lucrătoare) care nu prezintă diferențe la
nivel de mon taje, de acea descrierea celor 2 noduri va fi realizată în comun , după cum sunt
prezentate în continuare :
Nodul RPI_DEVICE_1 și RPI_DEVICE_2 au la bază o placă de dezvoltare SBC (Single Board
Computer) peste care este atașat un SoC (System on Chip), mai e xact Raspberry Pi 3 Model B. Aceste
2 noduri au ca obiectiv monitorizarea a 4 parametri, temperatură, nivel de umiditate , presiune
atmosferică și nivel de gaz, dintr -o încăpere, cu ajutorul unui modul senzor de temperatură și umiditate
DHT22 și a unui modu l senzor de gaz MQ -2, a căror descriere a fost făcută în capitolul 1, subcapitolul
2. Comunicația între cele 2 module senzor și placa de dezvoltare este realizată prin intermediul pinilor
de comunicație specifici de pe componenta ultim menționată.

Figura 2.5 – Schema vizuală a montajului electric
Ca elemente componente ale acestor 2 noduri avem :
• Placă de dezvoltare SBC Raspberry Pi 3 Model B+ ;
• Modul senzor de temperatură și umiditate DHT22 ;
• Modul senzor de gaz MQ -2;
• Convertor Analog -Digital MCP3002 .

32
Interconectarea componentelor necesare necesare măsurătorilor va fi mult mai detaliată în schema
următoare.

Figura 2.6

Nodul de control are de asemenea și el la bază o placă de dezvoltare SBC (Single Board
Computer) , însă face parte din prima generație de astfel de plăci de dezvoltare, deci puterea lui de
calcul este puțin mai redusă. Din punct de vedere structural, nu exista alte componente electronice
atașate la pinii disponibili de pe placă, ci are rol de a găzdui aplicația principală, disponibilă grafic din
browser, și baza de date PostgreSQL în care se vor introduce de către nodurile worker parametrii
măsurați. Comunicația pentru ca nodurile worker să poată introduce date în baza de date aflată pe
nodul de control se face prin intermediul unei rețele lo cale, la nivel de socket deschis pe portul 5432
TCP.

33
3. Procesul de implementare al sistemului

În cadrul capitolului 3 se vor trata 2 secțiuni principale legat de procesul de implementare a părții
logice, după cum urmează .
• Implementarea componentei l ogice, care descrie logica de prelucrare a datelor spre a fi
vizualizate în interfața web ; de asemenea este important de menționat faptul că prezentarea se
face schematic, prin reprezentarea acestora ca etape ;
• Implementarea componentei fizice, care prezint ă tot din punct de vedere software , procesul de
colectare, de la modulele senzor fizice, datele aferente până în punctul în care sunt trimise în
baza de date , dar de asemenea și din punct de vedere hardware.
Tot în acest capitol vor fi prezentate pe etape, în funcție de secțiunea părinte, contribuțiile personale
aduse asupra sistemului prezentat. Se va discuta mult pe baza codului implementat în aplicații, însă
datorită dimensiunii codului implementat se va pune accept în special, pe sistemul de autentifica re și
pe partea de prelucrare a datelor pe care le vede utilizatorul deoarece aceste 2 aspecte constituie
obiectul aplicației principale.
Un alt aspect important referitor la fișierele sursă, care conțin codul este că acesta se regăsește de
asemenea și în anexele atașate lucrării prezentate, anexe la care se va face referire pe parcursul acestui
capitol.

3.1. Implementarea componentei logice
Înainte de a începe descrierea codului aferent componentei logice, trebuie menționată de asemenea
și structura bazei de d ate, care stă la baza aplicației și are o importanță ridicată.
• configurarea serviciilor necesare funcționării bazei de date
În primă fază s -a instalat pe nodul de control, al cărui rol a fost descris anterior în cadrul capitolului
2, un set de utilitare di n suita de pachete oferite de PostgreSQL, care au avut ca obiectiv asigurarea
prezenței serviciului efectiv de baze de date, un client care putea fi utilizat direct din linia de comandă ,
și un pachet adițional care conține diverse fișiere binare care facil itează într -o manieră mult mai optimă
lucrul cu baza de date. Următorul pas a fost de a configura accesul din exterior spre serviciul de baze
de date, si prin verificarea ca baza de date poate fi accesată prin port 5432 TCP, port standard alocat
pentru Pos tgreSQL într -un sistem bazat pe Linux; în continuare efectuându -se procesul de configurare
a datelor de autentificare spre baza de date.
În urma verificării ca baza de date poate fi accesată din exterior, s -a continuat prin a începe etapa
de implementare p ropriu zisă a acesteia, prin crearea bazei de date și a tabelelor unde vor fi stocate,
după caz, datele necesare.
În figura de mai jos (Figura 3.1) este prezentată schema baz ei de date, împreună cu tab elele
asociate . Codul SQL utilizat în creare acestor en tități se află în Anexa 1 .

34

Figura 3.1 Schema bazei de date + tab elele asociate
• configurarea mediului unde va fi găzduită aplicația principală
În cadrul acestei faze s -a pregătit sistemul de operare, prin instalarea de diverse dependențe
(atașate Anexei x x) necesare ca aplicația să ruleze. Procesul a început prin instalarea principalelor
librării descrise în cadrul capitolului 1, prin intermediul utilitarului pip, care este un sistem de
administrare al pachetelor de Python. De asemenea s -a realizat un meca nism de pornire al aplicației
când nodul de control este restartat sau oprit și pornit din nou, prin intermediul serviciului systemd
care administrează modul de pornire al aplicațiilor. Codul sursă a fost plasat în directorul
/opt/main_application din sist emul de fișiere Linux.

• implementarea și structurarea codului sursă al aplicației găzduite pe nodul de control
Din punct de vedere structural codul sursă este împărțit în felul următor:
❖ fișierul app.py – reprezintă fișierul sursă principal în care sunt def inite rutele pe care
poate sa le acceseze utilizatorul ;
❖ fișierul functions.py – în cadrul acestui fișier sursă se află toate funcțiile care
implementează funcționalitățile aplicației;
❖ fișierul test_connectivity.py – este un fișier cod sursă special care vi zează statusul
nodurilor worker, rezultat pe baza căruia se modifică disponibilitatea nodurilor în
tabela devices;
❖ directorul static – conține fișierele CSS necesare stilizării diverselor pagini web .
Fișierele prezente în acest director sunt :
o fișierele justgage.js și raphael -2.1.4.min.js conțin codul CSS care stau la
baza stilizării elementelor din Figura 1.XX ;

35
o fișierul loginstyle.css conține codul CSS necesar stilizării paginii de
autentificare în aplicație .
❖ directorul templates – conține fișierele care su nt vizualizate în browser și din punct
de vedere al conținutului prezintă cod în HTML, CSS, JavaScript și Jinja2 . Fișierele
prezente în acest director sunt :
o devices.html – conține codul sursă care stă la baza paginii principale ce oferă
informații despre d isponibilitatea nodurilor worker și valori în timp real a
parametrilor măsurați;
o login.html – conține codul sursă necesar pentru pagina de autentificare;
o menu.html – conține codul sursă aferent meniului principal, fișier părinte
pentru celelalte fișiere di n directorul curent;
o rpi_dev_one.html – conține codul sursă pentru pagina web asociată primului
nod worker;
o rpi_dev_two.html – conține codul sursă pentru pagina web asociată celui de -al
doilea nod worker
❖ directorul scripts – este un director special, în ca re sunt incluse diverse script -uri, de
asemenea dezvoltate tot în Python, care au ajutat în procesul de testare a diverselor
funcționalități
Structura descrisă mai sus nu a fost realizată într -o singură etapă, această structura luând
naștere pas cu pas oda tă cu progresul obținut în urma dezvoltării codului sursă. Putem spune că cele
mai complexe fișiere cod sursă sunt app.py și functions.py, deoarece în aceste 2 fișiere sunt
implementate toate funcțiile logice și tot ceea ce poate sa acceseze utilizatorul.
În principiu, majoritatea codului se învârte în jurul operațiilor CRUD (Create, Read, Update,
Delete) efectuate asupra bazei de date astfel încât să putem interoga astfel datele pe care le avem
stocate.
O să începem prin a descrie structura și funcționali tatea codului sursă din fișierul app.py,
deoarece cum am menționat mai devreme, acest fișier este un fișier de bază pentru aplicația de pe nodul
de control. Pentru a începe utilizarea librăriei Flask, despre care s -a discutat în capitolul 1, trebuie în
primul rând inclusă în cadrul fișierului principal prin utilizarea sintaxei: from flask import Flask .
Această linie de cod permite programatorului să instanțieze obiecte noi de tipul Flask. Un exemplu de
bucată de cod sursă de la care s -a plecat în vederea de zvoltării aplicației este prezent în figura de mai
jos.

Figura 3.2 Exemplu de start pentru o aplicație dezvoltată cu Flask

36

Codul prezentat în figura 3.2 este o particularizare la codul din secțiunea ”A Minimal Application”
[20] din documentația oficial ă a librăriei Flask și s -ar traduce în felul următor:
• Import din librăria flask ceea ce se numește un ”constructor” pentru obiecte de tip Flask,
obiecte în jurul căror se învârt toate aplicațiile care au la baza această librărie. Acest import
este urmat de o instanțiere a unui nou obiect, care va avea numele app, instanțierea făcându –
se pe modulul nativ, daca nu este utilizat nici un alt modul. Modulul nativ în acest caz este
__name__ care are ca valoare prestabilită ”__main__”.
• În continuare există ceea ce în Python se numește decorator peste o funcție, și aici mă refer la
linia de cod @app.route(‘/’) . Acest decorator indică faptul că în momentul în care
utilizatorul va accesa, http://[adresă_ip]:[port]/ atunci fun cția home() se va executa, returnând
astfel textul ” proiect licenta! ” în browser.
• Ultima parte a acestui cod este efectiv apelarea metodei run() peste obiectul app, care
pornește în spate un server bazat pe Werkzeug
Codul descris mai sus este de asemenea p rezent în fișierul app.py, existând doar mici modificări
la modul de execuție al serverului care este pornit în spate, precum utilizarea unui mod de logare al
erorilor mult mai extins și permiterea accesului din exterior la server prin simpla modificare a adresei
IP la care se leagă serverul web.
În capitolul 2, am discutat despre prezența unui sistem de autentificare programatic implementat,
și pe baza figurii 2.2 o să continuăm discuția, dar la nivel de cod. Din diagramă se poate deduce
faptul că la prima accesare utilizatorul va utiliza mereu ruta părinte ”/”, deci astfel va întâmpina și
sistemul de logare. Conform codului de jos putem deduce de asemenea ca dacă variabila logged_in
nu are o valoare de adevăr pozitivă, adică nu este True atunci utilizatoru l va fi redirecționat spre
pagina de autentificare.
@app.route('/') ## ruta părinte ”/”
def home():
if not session.get('logged_in'):
return render_template('login.html') ##dacă variabila logged_in nu este
true atunci se va face redirecționarea automată spre pagina de autentificare
else:
return render_template(" menu.html") ## dacă variabila logged_in are
valoarea true, atunci utilizatorul va fi redirecționat spre pagina principală
@app.route('/login', methods=['POST']) ## ruta de autentificare ”/”
def do_login():
## se va verifica faptul ca parola corespunde cu ceea ce în acest moment
este declarat static și la fel si numele de utilizator . Dacă cele 2 condiții
sunt verificate atunci valoarea variabilei logged_in va fi sch imbată în True,
și utilizatorul va fi redirecționat înapoi spre ruta părinte ”/”
if request.form['password'] == 'password' and request.form['username'] ==
'admin':
session['logged_in'] = True
else:
flash('wrong credentials!')
return home()

37
În figura 3.3 este prezentată interfața grafică a paginii de autentificare, urmată de o referință
spre anexa 2 unde se află codul sursă pentru această pagină.

Figura 3.3 Pagina de autentificare
În continuare o să discutăm pas cu pas despre modul în care este generat dinamic conținutul
fiecă rei paginii accesibile utilizatorilor. Vom începe prin a descrie modul în care pagina pentru
sistem este generată, împreună cu statusul fiecărui dispozitiv și cum se preiau ultimele înregistrări
efectuate în baza de date, care reprezintă ultimele valori măsurate astfel, oferind utilizatorului
posibilitate de a vizualiza în timp real modificările acestor parametrii. În figura 3.4 de mai jos este
prezent ă structura paginii de dispozitive înregistrate în sist em.

Figura 3.4 Pagina de dispo zitive, denumită și devices

38
Tabelul din această pagină este realizat în timp real, pe baza tabelei din baza de date, în care
sunt înregistrate toate dispozitivele, tabelă prezentă și în figura 3.1. Pentru a putea genera aces t tabel
este necesar mai întâi să avem lista cu toate nodurile înregistrate în sistem, asta însemnând ca în cod
a fost necesară implementarea unei funcții care interoghează baza de date și returnează o variabilă
care conține aceste date, care au fost forma tate anterior.
În ruta ”/devices” , cod prezent în anexa 3 a fișierului cod sursă app.py, se face o apelare a
unei funcții care se numeste get_devices_list(). Această funcție este prezentată mai jos și descrisă în
detaliu.
## funcția get_devices_list() din fișierul functions.py apelată în app.py
def get_devices_list():
with psycopg2.connect(host=' 192.168.1.110 ', port='5432', dbname='data -db',
user='postgres', password=' password ') as conn:
curs = conn.cursor()
curs.execute("""SELECT * FROM devices;""")
data = curs.fetchall()
curs.execute("""SELECT column_name FROM information_schema.columns
where table_schema='public' and table_name='devices'""")
col_description = [row[0] for row in curs]
## values = create_list( data, col_description)
elements = create_devices_dict(create_list, (data, col_description))
conn.close()
return elements

## bucată de cod din fișierul app.py cu apelarea funcției

devices = get_devices_list() ## se returnează acea listă cu obiecte de tip
dicționar care este trimisă în codul paginii web, spre un script de JavaScript
pentru generarea dinamică a tabelului menți onat anterior.
devices_var = json.dumps(devices, indent=2, separators=(', ', ': ')) ## se
realizeaza serializarea obiectului pentru a putea fi trimis ca un obiect de tip
JSON
return render_template("devices.html", devices_var=devices_var,
raspberrypi_one=stats_rpi_one,
raspberrypi_two=stats_rpi_two)

Pentru a putea avea acces la baza de date a fost necesar să se instanțieze un obiect temporar
denumit ”conn” prin intermediul căruia s -a instanțiat un alt obiect cursor. Acest obiect de tip cursor()
permite efectiv accesul la baza de date, pentru a putea e xecuta operații pe baza de date. Datorită
modului în care datele sunt returnate de acest obiect de tip cursor() la execuția unei operații , a fost
necesară implementarea a 2 noi funcții, una (create_devices_dict() ) care generează o listă de
dicționare (date de tipul cheie – valoare) având ca elemente înregistrările din baza de date , dar care
au fost prelucrare după un anumit format, și o altă funcție (create_list() ) care returnează la fel o listă
de dicționare dar datele nu au fost prelucrare pentru a putea fi utilizate în pagina web.
Pentru a nu încărca extrem de mult documentația cu cod sursă care va fi atașat atât în anexe
cât și pe CD -ul atașat lucrării, o sa fac o descriere succintă a ceea ce se întâmpla cu variabila
devices_var în momentul în care ajung e în pagina web, pentru a fi utilizată la generarea tabelului.

39
După ce variabila este trimisă valoarea acesteia este preluată, deserializată și prelucrată
pentru a putea fi utilizată într -o structură iterativă de tip for, pe baza căreia se generează tabel ul. Este
important de menționat că din figura 3.4 reiese și o stilizare a tabelului în funcție de disponibilitatea
pe care o are fiecare nod din sistem, acest aspect fiind de asemenea realizat tot în procesul de creare
dinamică a tabelului , verificându -se valoarea pe care o are coloana status din baza de date, urmând să
se ia decizia, prin instrucțiuni decizionale de tip if, ce culoare să se utilizeze.
Să revenim la a descrie restul de elemente din pagina generală a dispozitivelor , care
reprezintă un aspect interesant deoarece oferă statistici în timp real asupra ultimelor înregistrări
efectuate de nodurile worker în baza de date. Pentru a crea obiecte precum cel prezentat în figura
1.18, s -a recurs la utilizarea unei librării externe scrisă în JavaScript, n umită JustGage, fișiere care au
fost incluse în structura de fișiere a aplicației.

Figura 3.5 Element implementat prin utilizarea librăriei externe JustGage
Aceste obiecte ca și tabelul pe care l -am descris anterior, dețin valori generate dinamic prin
interogarea bazei de date în vederea extragerii informației utile pentru a îndeplini acest obiectiv.
Apelarea funcțiilor care returnează ultimele înregistrări se face tot în funcția decorată de ruta
”/devices” după cum urmează (este luată doar o bucată din cod, deoarece codul integral se află în
anexa 3)
stats_rpi_one = get_db_entries('rpi_data_1', 'rpi_1', 1)
stats_rpi_two = get_db_entries('rpi_data_2', 'rpi_2', 1)
## prin intermediul funcției get_db_entries se returnează ultima înregistrare
efectuată în ba za de date.
## semnătura funcției get_db_entries este, după cum urmează :
get_db_entries(numele_tabelei, numele_nodului, număr_de_înregistrări)

datele returnate în variabilele stats_rpi_one și stats_rpi_two sunt mai apoi
serializate pentru a fi trimise ca obiecte de tip JSON spre pagina web.

stats_rpi_two = json.dumps(stats_rpi_two, indent=2, separators=(', ', ': '))
stats_rpi_one = json.dumps(stats_rpi_one, indent=2, separators=(', ', ': '))

La fel, ca în descrierea generării dinamice a tabelului cu nod uri și aici o să evit să plasez
codul din pagina web datorită dimensiunilor considerabile și o să continui cu descrierea acestuia din
momentul în care ajunge în pagina web (frontend) și cum este utilizat.

40
În momentul în care datele sunt trimise în pagina w eb, spre codul sursă scris în JavaScript,
aceste doua variabile (n. stats_rpi_one și stats_rpi_two) sunt preluate, deserializate și prelucrate
pentru a putea fi utilizate în generarea elementelor de statistică. În continuare, sunt declarate
variabile pentr u fiecare element de acest tip, prezent în pagină, se realizează instanțierea acestora cu
ajutorul funcțiilor implementate deja în librăria externă urmând ca în cadrul acestui pas să se atribuie
și valorile preluate din baza de date acestor noi obiecte. În codul HTML toate aceste noi variabile vor
fi utilizate pe post de id -uri la adăugarea de elemente de tip div care vor fi vizibile în pagină la fel
cum apar și în figura 3.5.

Următoarea descriere va fi efectuată pentru pagina caracteristică fiecărui nod. Fiecare nod deține o
astfel de pagină însă nu există diferențe majore la nivel de implementare, singurele diferențe fiind
determinate de modul în care sunt apelate anumite funcții sau rute adiacente al căror rezultat
reprezintă o imagine, dar aceste aspect e vor fi discutate în detaliu în paginile care urmează.
O scurta vizualizare a modului în care este implementată pagina este disponibilă în figurile 3.6 si 3.7.
Sunt necesare 2 figuri deoarece într -o singură figură nu se poate face vizibil tot conținutul p rezent
din această pagină. Descrierea figurilor menționate anterior este după cum urmează:
• Figura 1.19 oferă informații despre p rima parte a paginii aferente fiecărui nod care conține
selectoare pentru intervalul de timp dorit, pentru selectarea intervalul ui de agregare și de
asemenea o scurtă statistică a ultimelor 10 măsurători înregistrate.
• Figura 1.20 oferă informatii despre a doua parte a paginii și conține graficele realizate
dinamic în urma selecției personalizate a unui interval de timp dorit, urmat ă de selecția unui
interval de agregare a datelor din intervalul selectat. În figură este relativ vizibil pe axa X a
fiecărui grafic faptul că distanța temporală între puncte este de 60Min. Acest aspect poate fi
usor modificat selectând un alt interval de agregare din elementele disponibile în prima parte
a paginii.

41

Figura 3.6

42

Figura 3.7

43
Descrierea pentru modul în care e generată această pagină, o sa continue din punct de vedere
al implementării codului și ceea ce se întâmpla la rularea acestuia, de la interogare a bazei de date,
până la logica de prelucrare a datelor pentru a putea fi trasate graficele și pentru a le face vizibile în
pagina prezentată. O să descriem acest proces mult mai ușor pe baza unei diagrame logice a ceea ce
se întâmplă în spate, programatic vorbind.

Figura 3.8 – Diagrama logică a generării paginii atașate fiecărui nod worker
Pe baza figurii prezentate mai sus, 3.8 se va continua discuția în detaliu, realizată sub o structură
formată din mai multe etape, după va urma.
• Utilizator ul va accesa una din cele două rute asociate fiecărui nod înregistrat în sistem. În
momentul în care se accesează această rută, se va activa apelul metodei asociate, mai exact,
rpi_dev_x(), unde x reprezintă numărul de identificare al dispozitivului. La ap elul funcției
rpi_dev_x se vor executa un set de instrucțiuni care vor sta la baza generării primei părți din
pagină, observată și în figura 3.6, în urma elementelor generate din prima parte făcându -se
generarea celei de -a doua părți, din figura 3.7.
• Prima etapă reprezintă obținerea intervalului total de date înregistrat în baza de date, mai
exact, acest interval este reprezentat de perechea dată și ora la care a fost realizată prima
înregistrare, deci prima măsurare a parametrilor doriți, și perechea dată și oră la care a fost
realizată ultima înregistrare. Logica din spatele generării acestui interval este implementată în
funcția get_timestamps care ia ca argument numele tabelei din care vrem sa generăm acest
interval. Această funcție va returna în urma ex ecuției a 2 operații pe baza de date, 4
parametrii . Acești parametri au fost menționați mai sus, și fac obiectul generării celor 2
selectoare pentru dată și oră, atât pentru începerea, cât și pentru finalizarea intervalului de
timp dorit care va sta la baz a generării graficelor din partea a doua a paginii, din figura 3.7.
Obținerea acestor 2 perechi s -a realizat prin execuția a doua operații pe baza prezente mai jos,
si descrise de asemenea.

curs.execute("""SELECT * FROM %s ORDER BY (curr_date, curr_time) DESC LIMIT
1""", (AsIs(table),))
curs.execute("""SELECT * FROM %s ORDER BY (curr_date, curr_time) ASC LIMIT
1""", (AsIs(table),))

44

Operațiile prezentate anterior au ca scop interogarea completă a tabelei în cauză, ordonând
datele prima dată după dată (cur r_date), ulterior ordonându -se după timp/oră (curr_time), într -un
final fiind particularizate în funcție de caz. Pentru a obține prima pereche înregistr ă vreodată în
tabelă se va sorta crescător (ASC) și se va limita tot setul cerut la o singură intrare. A nalog pentru a
obține perechea ultim înregistrată se sortează descrescător (DESC) și se limitează setul pentru a
extrage ultima intrare în tabela.
• În a doua etapă, deoarece utilizatorul încă nu a selectat un interval dorit, se va genera în a
doua parte a p aginii setul de grafice care va conține absolut toate înregistrările efectuate în
tabelă agregate pe un interval de 60Min. De asemenea în acest caz, se va oferi utilizatorului o
listă de intervale de agregare de la 6 ore , din oră în oră, până la 60 minute , iar selecția pentru
unul dintre aceste intervale fiind prestabilită la 60 de minute. Deci cum se poate observa și în
figura 3.7, pe axa X sunt prezente date formate din perechea dată și oră, care are ca pas 60 de
minute.

Figura 3.9
Realizarea întregului proces de cerere a intervalului dorit și a intervalului de agregare al datelor se
face prin intermediul celor 3 elemente plus un buton de Trimitere selecții, prezente în figura 3.9.
• În momentul selecției unui interval de timp dorit urmat de trimiterea da telor selectate prin
apăsarea butonului ”Trimite” se va genera un request (cerere) de tip POST, din corpul c ăreia
se va face extragerea celor 2 perechi despre care s -a discutat anterior , perechea pentru data și
ora de început și perechea pentru data și ora de sfârșit. Aceste 4 date trimise vor fi stocate și
prelucrate pentru a putea fi utilizate în generarea graficelor. Mai jos este plasat și codul care
preia datele introduse de utilizator, le prelucrează și pe baza acestor date returnează setul de
grafice pentru cei 4 parametri și de asemenea sugerează o listă de intervale care pot fi folosite
pentru agregare.
start_date = datetime.datetime.strptime(request.form['start_date'], '%Y -%m-
%dT%H:%M:%S') ## se preia perechea pentru start interval
end_date = dateti me.datetime.strptime(request.form['end_date'], '%Y -%m-
%dT%H:%M:%S') ## se preia perechea pt finalul intervalului
selection = request.form['slct'] ## se preia de asemenea și valoarea pe care o
are selectorul, însă această variabilă va avea efect abia dupa c e se sugerează
lista de intervale de agregare
## parsez datele din datetime selector
sd = start_date.strftime("%Y -%m-%d") ## data de început
st = start_date.strftime("%H:%M:%S") ## ora de început
ed = end_date.strftime("%Y -%m-%d") ## data de sfârșit
et = end_date.strftime("%H:%M:%S") ## ora de sfârșit

45
diff, invl = suggest_sampling_time_interval('rpi_data_1', sd, st, ed, et)
## la apelarea funcției suggest_sampling_time_interval se returnează în
variabila diff numărul total de înregistrări și în variabila invl se stochează
lista cu intervale sugerate pentru agregare a datelor
Setul de grafice (figura 3.7) obținut în urma accesării pentru prima dată este generat de
apelul, în codul HTML al paginii fiecărui nod, al unei rute suplimentare, cu parametrii prestab iliți,
care returnează ca răspuns o imagine ce conține acest set de grafice. De exemplu, când utilizatorul
intră pentru prima dată apelul spre această rută arată în felul următor .
GET /plot/time_interval/ data?device_name=rpi_1&table_name=rpi_data_1&sd=2018 -09-
02&st=07:40:49&ed=2018 -09-04&et=07:39:49&selection=60Min HTTP/1.1" 200

Se poate deduce ușor că ruta apelată, la fel cum e menționată și în figura 3.8, este
/plot/time_interval/data, urmată de un set de parametrii după cum urmează :
1. device_name – ce ia ca valoare numele nodului
2. table_name – ia ca valoare numele tabelei din care se va extrage setul de date
3. sd, st – reprezintă perechea pentru data și ora de început a intervalului dorit
4. ed, et – reprezintă perechea pentru data și ora de sfârșit a intervalu lui dorit
5. selection – reprezintă valoarea dorită pentru agregarea datelor.
O explicație mai detaliată a request -ului (cererii) pentru ruta /plot/time_interval/data cu
parametrii prestabiliți anterior, ar fi următoarea. Se va extrage pe baza parametrilor pr imiți setul de
date cuprins în intervalul (2018 -09-02T07 :40:49 – 2018 -09-04T07:39:49) rezultând un total de 2880
de înregistrări, deoarece datele sunt trimise în baza de date la un interval de 1 minut. Folosind o
selecție a intervalului de agregare de 60Mi n, adică 1 oră, rezultă 48 de puncte care vor fi prezente pe
grafice.
Generarea acestor grafice a fost posibilă prin utilizarea a 2 librării externe, care au fost descrise în
capitolul 1, și anume: matplotlib și pandas.
Matplotlib a ajutat la generarea ace stor grafice, printr -o sintaxă asemănătoare cu cea scrisă în
MATLAB . Pașii efectuați în obținerea graficelor într -un singur ”plot” utilizând așa numitele
”subplots” sunt intuitivi și descriși mai jos .
1. S-au creat 5 liste separate, dintre care 4 liste au fos t utilizate pentru a stoca valorile măsurate,
obținute prin agregare și o a 5-a listă care a fost utilizată pentru a stoca intervalele de timp
necesare pentru a trasa graficele , într -un final fiecare grafic reprezentând o trasare prin puncte
de forma (time stamp; valoare_agregată) .
temps = [x["temperature_avg"] for x in elements]
hums = [x["humidity_avg"] for x in elements]
gas = [x["gas_level_avg"] for x in elements]
pres = [x["gas_level_avg"] for x in elements]
dates = [x["datetime"] for x in elements]

46
2. S-a instanțiat un obiect de tipul Figure în care vor vi plasate pe rând graficele vizualizate în
pagina web. Dimensiunile figurii au fost alese după rezultatul mai multor teste astfel încât să
suporte 3 subgrafice fără ca acestea să se suprapună și să devin ă ilizibile.
_figure = Figure(figsize=(19, 9.5), facecolor="#c6dcff")
_figure.tight_layout()
_figure.subplots_adjust(hspace=0.8, bottom=0.125)

3. Pasul următor a constat în setarea axelor X și Y în funcție de ceea ce o sa se afle reprezentat
pe ele. În cazu l axei X, deoarece reprezinta variabile compuse din perechea dată și oră a fost
necesară o formatare a listei dates , care conține aceste perechi, astfel încât să aibă un format
utilizabil de matplotlib în vederea realizării graficelor. Tot la acest pas s -au alex și limitele pe
axa Y pentru cele 4 subgrafice în funcție de valoarea minimă și maximă din cele 4 liste cu
măsurători.
## se setează formatul pentru lista cu perechi dată și oră
x_format = matplotlib.dates.DateFormatter('%Y -%m-%d %H:%M:%S')

## s-a setat pe axa X ce listă de elemente să utilizeze și sub ce rotație
să fie afisate.
axis_temp.set_xticklabels(dates, rotation=20)
axis_hum.set_xticklabels(dates, rotation=20)
axis_gas.set_xticklabels(dates, rotation=20)
axis_pres.set_xticklabels(dates, rotat ion=20)

## s-au setat limitele pe axa Y pentru cele 4 grafice, în funcție de
minimul și maximul din fiecare listă
axis_temp.set_ylim([(min(temps) -3),(max(temps)+3)])
axis_hum.set_ylim([(min(hums) -10),(max(hums)+10)])
axis_gas.set_ylim([(min(gas) -50),(max( gas)+50)])
axis_pres.set_ylim([(min( pres)-50),(max( pres)+50)])

4. Procesul de returnare al rezultatului final a fost reprezentat de obținerea unei imagini sub
format PNG prin intermediul instanțierii unui obiect de tip FigureCanvas, care reprezintă
efectiv zo na unde obiectul de tip Figure este desenat. În vederea utilizării acestei imaginii s -a
decis utilizarea unui strem de bytes, obținut prin utilizarea librăriei io, si returnarea acestui
stream de bytes sub tipul de ”image/png” .
Mecanismul de prelucrare al datelor utilizate în generarea graficului s-a realizat prin intermediul
librăriei externe pandas. Înainte de a începe a procesa aceste date cu ajutorul funcționalităților oferite
de librăria pandas a fost necesară extragerea lor din baza de date și de a re turna aceste date sub un
format mult mai usor de prelucrat. Mai jos este prezentă o figură în care apare formatul final al
datelor, care ulterior vor fi prelucrare în pandas pentru a genera agregarea în funcție de intervalul de
timp.

47

Figura 3.10
Structur a datelor prezentat ă în figura 3.10 este o structură ușor de utilizat datorită modului
eficient de accesare pe bază de index, urmat de cheie, deoarece variabila final_data este defapt o listă
care conține dicționare , iar dicționarele sunt tipuri de date de forma cheie – valoare.
În continuare aceste date sunt transformate prin ajutorului structurii de date DataFrame pusă la
dispoziție de paandas. DataFrame putem spune ca este echivalentul Excel -ului, dar care poate fi
modificat mult mai ușor și prezintă mul t mai multe funcționalități. Deoarece în baza de date data și
ora apar ca 2 coloane separate, pentru a putea fi utilizate de matplotlib ca elemente în funcție de care
trasează graficele finale, cu ajutorul pandas s -a realizat o concatenarem, între coloana curr_date și
coloana curr_time, prin care s -a generat o nouă coloană, datetime, renunțându -se la final de
coloanele care s -au concatenat. Procesul de prelucrare este continuat prin formatarea coloanei nou
create astfel încât să corespundă întradevăr cu dat e de tipul timestamp . În continuare, pentru a realiza
agregarea propriu zisă, pandas oferă în continuare o funcționalitate interesantă care permite gruparea
datelor pe baza intervalelor de timp. Ca această funcționalitate să poată fi utilizată corect, inde xarea
setului de date din DataFrame trebuie sa fie pe o coloană care are un tip de dată format din dată și
oră, fix ceea ce s -a realizat mai devreme. Exact această funcționalitate a fost utilizată în a obține
aceste agregări.
grpby_time_df = df.groupby(pd. TimeGrouper(time_interval))['temperature',
'humidity', 'gas_level'].mean().add_suffix('_avg').reset_index()
Linia de cod pusă s -ar traduce în felul următor:
Pe setul de date din variabila df se va realiza o grupare în funcție de time_interval, pe coloanel e
de temperatură, umiditate , presiune atmosferică și nivel de gaz. În continuare dupa ce se grupează
aceste date se realizează o medie a acestora, adăugându -se pentru noile coloane create sufixul _avg,
noile coloane fiind următoarele:
• temperature_avg;
• humi dity_avg;
• gas_level_avg.
Pe setul nou de date obținut se va reseta indexul, deoarece nu mai avem nevoie de acesta pentru
că prelucrarea s -a efectuat . În continuare DataFrame -ul cu setul de date prelucrat este converit într -o
listă de dicționare pentru a pu tea fi, la fel, ușor de utilizat în continuare.
Una dintre cele mai importante aspecte pe care am pus accent în partea finală a proiectului a fost
de a realiza o pagină specială care să conțină, la fel, un set de 4 grafice, pentru cei 4 parametrii
măsurați . Diferența între paginile nodurilor worker și această pagină o reprezintă faptul că în această
pagină nu mai este trasat pe cele 4 grafice doar măsurătorile efectuate de un dispozitiv, ci sunt
plasate în funcție de grafic toate seturile de măsurători real izate de cele 2 dispozitive. Mai exact, pe

48
graficul de temperatură vor exista în cazul de față, 2 trasări pe baza a 2 seturi de date provenite de la
fiecare dispozitiv în parte.
Pagina care prezintă grafic felul în care variază acești parametrii, în funcți e de încăperea în care
se află nodurile oferă o viziune mai îndetaliată asupra acestui aspect.
De asemenea, în această pagină s -a implementat același sistem de prelucrare al datelor și de
agregare al acestora, permițând astfel utilizatorului să își aleagă personal pe ce perioadă de timp
dorește să compare datele înregistrate de aceste noduri.
În partea de implementare propriu zisă, cum am menționat mai sus, procesul a fost asemănător,
diferențele apărând la nivel de configurare al funcționalităților oferite de librăria matplotlib și de a
mai include, în plus, un nou set de date, dar care să fie trasat pe același grafic cu cel inițial.
Într-o astfel de aplicație, unde dispozitivele sunt localizate în încăperi diferite, dar în aceași
clădire, cum a fost și sit uația sub care codul a fost dezvoltat, s -a decis că ar fi recomandată de altfel și
o pagină specială care ia datele de la ambele dispozitive trasează în grafice seturile fiecărui
parametru măsurat. În anexa 7 es te prezentă o imagine care afi șează cele 4 grafice în care sunt trasate
seturile de date pent ru ambele noduri din sistem. Pentru a realiza și această funcționalitate s -a plecat
ca în cazurile precedente de la un cod de bază pentru realizarea graficelor și s -a implementat rând cu
rând această funcționalitate. Execuția codului care stă la baza acestei pagini ar fi următoare :
▪ se preia intervalul de timp specificat de utilizator, împreu na cu intervalul de agregare al
datelor;
▪ se interoghează baza de date astfel încât să se obțină seturile de date cuprinse în intervalul de
timp specificat inițial;
▪ se prelucrează datele în așa manieră încât să poată fi pregătite pentru trasarea graficelor;
▪ se utilizează, ca în cazul paginii fiecărui nod, librăria matplotlib și se generează setul de
grafice pentru fiecare parametru .

3.2. Implementarea componentei fizice
În acest subcapitol se va face prezentarea procesului de implementare din punct de vedere
programatic al citirii valorilor măsurate de senzori, prelucrarea datelor , pe nodurile worker, și
trimiterea acestora spre baza de date localizată pe nodul de control. De asemenea se va începe prin a
fi prezentat mai întâi procesul de configurare minim al înt regului sistem.
• Instalarea și configurarea sistemului de operare pe plăcile de dezvoltare. Activarea capabilității
de control prin SSH de la prima pornire a dispozitivelor. Conectarea inițială cu cabluri Ethernet
la un echipament de rețea care să aibă capa bilități de a oferi adrese IP în mod dinamic, prin
intermediul protocolului DHCP. Descoperirea adreselor IP alocate dispozitivelor prin
intermediul trimiterii pachetelor de tip ARP . Odată obținută adresa IP s -a trecut rapid la
configurarea chipurilor de Wi Fi pentru plăcile de dezvoltare Raspberry Pi 3 Model B+ pentru
a obține static o adresă IP alocată astfel reducând timp de cautare a unei adrese IP alocate
dinamic.

49
• Implementarea propiu zisă a montajului, urmată de o testare scurtă că montajele sunt corect e
prin intermediul scripturilor care vor fi descrise în continuare, astfel încât modulele senzor să
raporteze corect date.
Procesul de implementare din punct de vedere software a început prin configurarea mediului de
lucru, care a presupus instalarea unor librării externe care să permită citirea datelor de la senzori și
configurarea serviciul de fus orar astfel încât sa nu se utilizeze un fus orar greșit, deoarece acest lucru
s-ar fi reflectat asupra datelor insertate în baza de date și implicit, ar fi crea t confuzie în momentul
utilizării aplicației principale când s -ar fi vizualizat graficele dorite.
Acest proces a fost continuat prin implementarea de scripturi separate care să testeze montajul
realizat și descris în cadrul capitolului 2. S -a dorit testare a separată a componentelor care aveau ca
scop monitorizarea, astfel:
• Implementarea unui script care să citească valorile măsurate de modulul senzor de temperatură
și umiditate DHT22. Acest script a avut la bază utilizarea unei librării externe (Adafruit_DH T),
cu funcții deja implementate eficientizând astfel procesul de testare. Dezvoltarea s -a realizat
rapid datorită exemplelor puse la dispoziție de această librărie, singura contribuție adusă acestui
script fiind doar formatarea datelor.
• Implementarea unui alt script care să citească valorile furnizate de modulul senzor de gaz MQ2,
care s -a dovedit a prezenta un grad de dificultate mai sporit, în comparație cu scriptul anterior,
deoarece nu a existat o librărie propriu zisă care să implementeze funcționalit ățile dorite.
Citirea datelor furnizate de MQ2 nu s -a facut direct la un pin al senzorului, ci s -a făcut indirect,
datele furnizate de MQ2 fiind prima dată converite din analog în digital prin intermediul
convertorului analog -digital MCP3002. Implementarea codului pentru citirea canalelor
convertorului a fost ușurată de utilizarea [xx] unui cod deja implementat, singurele contribuții
aduse la acest script fiiind de verificare a funcționalității, urmate de o scurtă revizuire a codului.
Cele 2 scripturi descr ise mai sus, care au făcut posibilă citirea datelor furnizate de modulele senzor
au stat la baza realizării mini aplicației plasată pe fiecare din nodurile worker. Aplicația introduce în
baza de date la un interval de aproximativ un minut cu +(5, 20) secun de, marjă de eroare datorată
latenței introdusă de comunicarea în rețea și de apelare efectivă a funcțiilor. Citirea se face la un
interval de 19.33 secunde, astfel încât la un interval de 1 minut să existe mereu un set de 3 măsurători
utilizate pentru a f ace o medie aritmetică între ele ca ulterior să fie introdusă în baza de date. S -a ales
introducerea rezultatului medii între 3 seturi de măsurători pentru a reduce din posibilele erori de citire
care pot surveni.
gas_one = read_smoke_level() ## se stochea ză în memorie primul set de valori
temp_one, hum_one = read_dht_sensor(sensor_type, pin)
pres_one = read_pressure()
logger.debug('Records: \ttemperature: %s[C] \thumidity: %s[procent] \tgas_level:
%s ppm\tpressure: %s mmHg ' % (temp_one, hum_one, gas_one , pres_one))
sleep(19.33)
gas_two = read_smoke_level() ## se stochează în memorie setul 2 de valori
temp_two, hum_two = read_dht_sensor(sensor_type, pin)
pres_two = read_pressure()
logger.debug('Records: \ttemperature: %s[C] \thumidity: %s[procent] \tgas_level:
%s ppm\tpressure: %s mmHg ' % (temp_two, hum_two, gas_two , pres_two ))
sleep(19.33)

50
gas_three = read_smoke_level() ## se stochează în memorie setul 3 de valori
temp_three, hum_three = read_dht_sensor(sensor_type, pin)
pres_three = read_pressure()
logger.debug ('Records: \ttemperature: %s[C] \thumidity: %s[procent] \tgas_level:
%s ppm\tpressure: %s mmHg ' % (temp_three, hum_three, gas_three , pres_three ))
sleep(19.33 )
mean_gas_level = (gas_one + gas_two + gas_three) / 3
mean_temp = (temp_one + temp_two + temp_three) / 3
mean_hum = (hum_one + hum_two + hum_three) / 3
mean_pres = (pres_one + pres_two + pres_three) / 3

În cadrul bucății de cod atașată se pot observa de asemenea și prezența unor linii de cod care
ajută la înregistrarea acestor date pentru o posibilă an alizare a acestora, în cazul în care unul dintre
modulele senzor nu mai măsoară date și returnează valori nule.
Deoarece s -a dorit ca nodurile să aibă statusul de disponibilitate mereu actualizat în baza de
date, pe lângă procesul de colectare al parametri lor de la senzori, s -a implementat un sistem multi -fir
de execuție , simplu , care pornește un fir de execuție de colectare al datelor urmat de un alt fir de
execuție care deschide o rută a unui server web dezvoltat tot în Flask pe un port prestabilit , și
funcționează conform figurii prezentate mai jos.

Figura 3.8 Diagrama

Codul complet al aplicației localizată pe cele 2 dispozitive este prezent pe CD și în anexa 5.

51
Concluzii
Ceea ce s -a propus inițial în momentul alege rii temei de lucru, reprezentând procesul de
proiectare și implementare propriu zisă a sistemului a reprezentat un succes obținut prin studiu și timp
invenstit în a atinge scopul final al acestei lucrări.
Realizarea din momentul realizării montajelor pent ru cele 2 noduri care înregistrau datele
dorite, lucrului cu baze de date și extragerea de informație utilă și mai ales de prelucrare propriu zisă
a acestor date a necesitat de altfel un bagaj amplu în ceea ce privește realizarea de elemente grafice și
de plasare a acestora în pagină, baze de date, pentru realizarea unei scheme de lucru optime, limbaje
de programare și cunoașterea sistemelor de operare, electronică de bază, ușor de înteles, dar mai ales
dorința de a realiza de la zero o aplicație care să im plementeze aceste funcționalități.
Utilitatea sistemului prezentat de -a lungul acestei lucrări o constituie funcționalitatea pe care o
oferă utilizatorului de a își alege singur ce intervale de timp își dorește, fără a fi condiționat de a alege
doar 1 zi, 1 săptămână sau pe sezoane. Consider că această funcționalitate implementată este una
inovativă, poate deja implementată, dar am dorit a realiza această funcționalitate după o viziune
proprie, fără a știi în prealabil alte sisteme care deja prezintă aceas tă funcționalitate.
Din punct de vedere al aspectului fizic final, s -a dorit a se compacta cât mai mult, dar datorită
și dimensiunilor mai mari ale plăcilor de dezvoltare alese în comparație cu altele, rezultatul nu a fost
chiar tocmai cel mai dorit, dar ș i pe baza a ceea ce s -a obținut s -au efectual simple modificări la modul
în care componentele necesare au fost plasate.
Avantaje ale sistemului implementat și prezentat în lucrare:
• Sistemul deține funcționalitatea de alegere personalizată a datelor pe care utilizatorul
dorește să le verifice;
• Chiar dacă sistemul este localizat într -o rețea locală de internet, prezența unui sistem de
autentificare pe bază de nume de utilizator și parolă aduce un plus pe partea de
securitate ;
• Toate elementele necesare ca date le oferite de utilizator să fie corecte au fost
implementate cât mai optim și separate în 2 fișiere fiecare cu utilitatea lui astfel oferind
programatorului o viziune mai clară asupra codului implementat ;
• Aplicația deține și o interfață grafică, relativ si mplă, cu indicații despre pașii pe care
trebuie să îi urmeze utilizatorul în vedea obținerii datelor dorite ;
• Aplicația secundară localizată pe nodurile worker prezintă un sistem de pornire automat
în momentul în care unul dintre noduri este oprit si pornit la loc.
Dezavantaje ale sistemului prezentat în lucrare:
• Adăugarea de noi noduri în sistem prezintă un grad destul de ridicat de dificultate,
intervenția specializată fiind extrem de recomandată în acest caz ;
• În cazul de față sistemul este izolat, utiliza torul neavând capabilitatea de a putea accesa
interfața prin intermediul unei conexiuni la internet active ;
• Din punct de vedere financiar dacă se utilizează componente noi originale costurile pot
să se ridice considerabil pentru fiecare nod nou adăugat.

52
De-a lungul implementării întregului sistem, acesta a fost supus testării , în special nodurile care
stau la baza colectării de date , aceștia fiind lăsați intervale scurt de 24 -48 de ore sa colecteze date, fără
ca să existe probleme la nivelul componentelor. Din punct de vedere al utilizării bazei de date, deoarece
în majoritatea aplicațiilor care utilizează o astfel de capabilitate de stocare, nu au existat probleme de
conectare sau de a rămâne fără conexiuni active spre aceasta, acest aspect fiind unul dint re primele
lucruri care au fost vizate în momentul dezvoltării codului pentru a nu apărea pe parcurs.
Prin realizarea procesului de testare a sistemului ca un întreg s -au realizat pe parcurs diverse
modificări la modul cum graficele sunt afișate în interfa ța grafică, la nivel de aspect al interfeței grafice,
care a presupus în special o stilizare minimă de a plasa elementele cât mai optim în pagină. De
asemenea tot de -a lungul acestui proces de testare s -a efectuat modificări la nivel de aspect fizic, astfe l
încât nodurile worker să aibă o formă finală cât mai compactă. De asemenea inițial s -a dorit conectarea
dispozitivelor în rețea pentru a micșora latența care poate apărea în cazul utilizării unor conexiuni
wireless, acest aspect fiind testat și s -a ajuns la concluzia ca diferențele apărute care să impacteze
performanța sistemului sunt extrem de scăzute, astfel s -a renunțat la seturi de cabluri care puteau
îngreuna utilizarea sistemului.
În închieierea concluzilor va fi menționat faptul că sistemul este un ul robust, implementarea
finală chiar dacă atinge scop acestei lucrări este de departe a fi perfectă, fiind necesare modificări atât
din punct de vedere fizic pt o mai bună compactizare, cât și din punct de vedere logic pentru
optimizarea și reducerea timp ilor de prelucrare ai datelor.
Direcții de dezvoltare pe viitor a sistemului prezentat
• Optimizarea codului și a metodelor de prelucrare a datelor, pentru a reduce timpii necesari
acestor operațiuni.
• Implementarea unui sistem de automatizare pentru integra rea automată în aplicația principală
la momentul adăugării de noi noduri.
• Implementarea unui nivel de securitate mult mai ridicat, realizat prin implementarea de
certificate SSL, astfel încât transferul de date între utilizator și aplicație să fie securiza t pe baza
acestui protocol.
• Integrarea unui sistem de alertă prin configurara unui serviciu de mail și utilizarea unui API
extern de trimitere mesaje direct pe telefonul utilizatorului în cazul în care se depășesc anumite
limite ale parametrilor.
• Modificar ea sistemului astfel încât să asigure redundanță în cazul pierderi nodului de control,
prin păstrarea stării sistemului și într -o locație externă, singurele diferențe apărând la nivelul
nodurilor worker care vor fi redirecționate spre noua locație.
• Integra rea unei funcționalități care să elimine automat din interfața grafică nodurile și paginile
lor în momentul în care nu mai sunt disponibile.

53
Bibliografie
[1] Learning t o program with Python – Richard L. Halterman
https://www.cs.uky.edu/~keen/115/Haltermanpythonbook.pdf
[2] Python 2.7 documentation – https://docs.python.org/2.7/tutorial/appetite.html
[3] Pym book – https://pymbook.readthedocs.io/en/latest/flask.html
[4] Flask documentation – http://flask.pocoo.org/docs/1.0/foreword/
[5] Text introductiv – John D. Hunter, autorul original al matplotlib
https://matplotlib.org/users/history.html
[6] Matplotlib documentation – http://pandas.pydata.org/pandas -docs/stable/
[7] Psycopg documentation – http://initd.org/psycopg/docs/
[8] Noțiuni de bază HTML
https://developer.mozilla.org/ro/docs/Learn/Getting_started_with_the_web/HTML_basics#Ce_este_
HTML_de_fapt
[9] Ce este CSS -ul? – Introducere în CSS – Tutoriale CSS https://it.webdesign -galaxy.ro/ce -este-css/
[10] Ce este JavaScript? –
https://developer.mozilla.org/ro/docs/Learn/JavaScript/First_steps/ Ce_este_JavaScript
[11] Noțiuni despre bazele de date folosind PostgreSQL
https://docs.qgis.org/2.8/ro/docs/training_manual/database_concepts/index.html
[11] Ce este raspberry pi ? digipedia https://digipedia.ro/raspberry -pi-3-ce-este-raspberry -pi/
[12] Specificații – Raspberry Pi https://en.wikipedia.org/wiki/Raspberry_Pi#Specifications
[13] Raspberry Pi 3 Model B+ https://static.optimusdigital.ro/20252 -thick box_default/raspberry -pi-3-
model -b-plus.jpg
[14] Sparkfun – Raspberry Pi 1 – Model B https://www.sparkfun.com/products/retired/11546
[15] DHT22 – senzor de temperatură și umiditate https://www.robotshop.com/en/dht22 -temperature –
humidity -sensor.html
[16] MQ -2 – senzor de gaz https://www.optimusdigital.ro/ro/senzori -de-gaze/107 -modul -senzor -gas-
mq-2.html
[17] BMP180 – https://www.optimusd igital.ro/ro/senzori -senzori -de-presiune/149 -modul -senzor -de-
temperatura -si-presiune -bmp180.html
[18] MCP3002 – Farnell https://ro.farnell.com/microchip/mc p3002 -i-p/ic-adc-10bit -200ksps -pdip-
8/dp/1852015
[19] SDLC models – Software development lifecycle models https://existek.com/blog/sdlc -models/
[20] A Minimal Application http://flask.pocoo.org/docs/1.0/quickstart/#a -minimal -application

54

55
Anexa 1
## create tablă rpi_data_1
CREATE TABLE public.devices
(
device_id bigserial NOT NULL,
device_name character
varying(25) NOT NULL,
status character varying(20)
NOT NULL,
PRIMARY KEY (device_id)
)
WITH (
OIDS = FALSE
);
ALTER TABLE public.devices
OWNER to postgres;

## create tablă rpi_data_ 1
CREATE TABLE public.rpi_data_ 1
( data_id bigserial NOT NULL,
temperature numeric(4, 2) NOT
NULL,
humidity numeric(4, 2) NOT
NULL,
gas_level numeric(6, 2) NOT
NULL,
pressure numeric(8, 2) NOT
NULL,
curr_date date NOT NULL,
curr_time time without time
zone NOT NULL,
device_name character
varying(25) NOT NULL,
PRIMARY KEY (data_id)
)
WITH (
OIDS = FALSE
);

## create tablă rpi_data_2
CREATE TABLE public.rpi_data_ 2
(
data_id bigserial NOT NULL,
temperature numeric(4, 2) NOT
NULL,
humidity numeric(4, 2) NOT
NULL,
gas_level numeric(6, 2) NOT
NULL,
pressure numeric(8, 2) NOT
NULL, curr_date date NOT NULL,
curr_time time without time
zone NOT NULL,
device_name character
varying(25) NOT NULL,
PRIMARY KEY (data_id)
)
WITH (
OIDS = FALSE
);

56

57
Anexa 2
## cod sursă pagina de autentificare
<link rel="stylesheet"
href="../static/loginstyle.css"
type="text/css">
{% block body %}
{% if session['logged_in'] %}
<p>You're logged in already!</p>
{% else %}

<form action="/login" method="POST">
<div class="login">
<div class="login –
screen">
<div
class="app -title">

<h1>Login</h1>
</div>

<div
class="login -form">
<div
class="control -group">
<input
type="text" class="login -field"
value="" placeholder="username"
name="username">
<label
class="login -field-icon fui -user"
for="login -name"></label>
</div>

<div
class="control -group">
<input
type="password" class="login -field"
value="" placeholder="password"
name="pass word">
<label
class="login -field-icon fui -lock"
for="login -pass"></label>
</div>

<input type="submit"
value="Log in" class="btn btn -primary
btn-large btn -block" >
<br>
</div>
</div>
</div>
</form>

{% endif %}
{% endblock %}

## codul de stilizare al paginii de
autentificare.
## PRECIZEZ CĂ NU IMI APARȚINE,
EXISTÂND DOAR MODIFICĂRI ALE
ACESTUIA.
* {
box-sizing: border -box;
}

*:focus {
outline: none;
}
body {
font-family: Arial;
background -color: #111;
padding: 50p x;
}
.login {
margin: 20px auto;
width: 300px;
}
.login-screen {
background -color: #FFF;
padding: 20px;
border-radius: 5px
}

.app-title {
text-align: center;
color: #777;
}

.login-form {
text-align: center;
}
.control -group {
margin-bottom: 10px;
}

input {
text-align: center;
background -color: #ECF0F1;
border: 2px solid transparent;
border-radius: 3px;
font-size: 16px;
font-weight: 200;
padding: 10px 0;
width: 250px;
transition: border .5s;
}

input:focus {
border: 2px solid #3498DB;
box-shadow: none ;
}

.btn {

58
border: 2px solid transparent;
background: #3498DB;
color: #ffffff;
font-size: 16px;
line-height: 25px;
padding: 10px 0;
text-decoration: none;
text-shadow: none;
border-radius: 3px;
box-shadow: none;
transition: 0.25s;
display: block;
width: 250px;
margin: 0 auto;
}

.btn:hover {
background -color: #2980B9;
}

.login-link {
font-size: 12px;
color: #444;
display: block;
margin-top: 12px;
}

59
Anexa 3
import matplotlib
import numpy as np
import datetime

#from datetime import datetime
from functions import *
from matplotlib.backends.backend_agg
import FigureCanvasAgg as
FigureCanvas
from matplotlib.figure import Figure

app = Flask(__name__)
api = Api(app)

app.config[' JSON_SORT_KEYS'] = False

## main route, which based on session
status will redirect to login page or
to main menu page
@app.route('/')
def home():
if not session.get('logged_in'):
return
render_template('login.html')
else:
return
render_template("menu.html")

## login route, which compare the
input wrote by user with these
hardcoded values
@app.route('/login',
methods=['POST'])
def do_login():
if request.form['password'] ==
'password' and
request.form['username'] == 'admin':
session['logged_in'] = True
else:
flash('wrong credentials!')
return home()

## logout route
@app.route('/logout')
def logout():
session['logged_in'] = False
return home()

## /devices route, which return a
table with all devices registered,
and their status, and few
gauges(graphics)
## with the newest read @app.route('/devices')
def get_devices():
devices = get_devices_list()
## returns latest readings
stats_rpi_one =
get_db_entries('rpi_data_1', 'rpi_1',
1)
stats_rpi_two =
get_db_entries('rpi_data_2', 'rpi_2',
1)

## ld -> ultimul timestamp
inserat in db
## fd -> primul timestamp inserat
in db
ld_curr_date, ld_curr_time,
fd_curr_date, fd_curr_time =
get_timestamps('rpi_data_1')
print ld_curr _date
print ld_curr_time
print fd_curr_date
print fd_curr_time

stats_rpi_two =
json.dumps(stats_rpi_two, indent=2,
separators=(', ', ': '))
stats_rpi_one =
json.dumps(stats_rpi_one, indent=2,
separators=(', ', ': '))
devices_var = json.dumps(devices,
indent=2, separators=(', ', ': '))
return
render_template("devices.html",
devices_var=devices_var,
raspberrypi_one=stats_rpi_one,
raspberrypi_two=stats_rpi_two)

@app.route('/data', methods=['GET',
'POST'])
def data():
## retri eve dates from user
choice
if request.method == 'POST':
start_date =
datetime.datetime.strptime(request.fo
rm['start_date'], '%Y -%m-%dT%H:%M')
end_date =
datetime.datetime.strptime(request.fo
rm['end_date'], '%Y -%m-%dT%H:%M')

if request.method == 'POST':
sp_rpi_1 =
request.form['samples_rpi_1']
sp_rpi_2 =
request.form['samples_rpi_2']

60
time_division_rpi_1 =
get_frequency_sample('rpi_data_1',
'rpi_1')
time_division_rpi_2 =
get_frequency_sample('rpi_data_2',
'rpi_2')
return
render_template("data.html",
td_rpi_1=time_division_rpi_1,
td_rpi_2=time_division_rpi_2)

@app.route('/rpi_dev_one',
methods=['GET', 'POST'])
def rpi_dev_one():

ld_curr_date, ld_curr_time,
fd_curr_date, fd_curr_time =
get_timestamps(' rpi_data_1')
ld_curr_time, fd_curr_time =
ld_curr_time.strftime("%H:%M:%S"),
fd_curr_time.strftime("%H:%M:%S")

sd, st, ed, et = fd_curr_date,
fd_curr_time, ld_curr_date,
ld_curr_time
samples = ['360Min', '300Min',
'240Min', '180Min', '120Min',
'60Min']

selection = '60Min'

if request.method == 'POST':
## iau datele din datetime
selector
start_date =
datetime.datetime.strptime(request.fo
rm['start_date'], '%Y -%m-
%dT%H:%M:%S')
end_date =
datetime.datetime.strptime( request.fo
rm['end_date'], '%Y -%m-%dT%H:%M:%S')
selection =
request.form['slct']
## parsez datele din datetime
selector
sd = start_date.strftime("%Y –
%m-%d")
st =
start_date.strftime("%H:%M:%S")
ed = end_date.strftime( "%Y-
%m-%d")
et =
end_date.strftime("%H:%M:%S")
diff, samples =
suggest_sampling_time_interval('rpi_d
ata_1', sd, st, ed, et)

ld_curr_date = ed
ld_curr_time = et
fd_curr_date = sd fd_curr_time = st

return
render_template("rpi_dev_one.html",
fd_curr_date=fd_curr_date,
fd_curr_time=fd_curr_time,
ld_curr_date=ld_curr_date,
ld_curr_time=ld_curr_time,
start_date=sd, start_time=st,
end_date=ed, end_time=et,
spl=map(json.dumps, samples),
selection=selection)

@app.route('/rpi_dev_two',
methods=['GET', 'POST'])
def rpi_dev_two():
ld_curr_date, ld_curr_time,
fd_curr_date, fd_curr_time =
get_timestamps('rpi_data_2')
ld_curr_time, fd_curr_time =
ld_curr_time.strftime("%H:%M:%S"),
fd_curr_time.strftime("%H:%M: %S")

sd, st, ed, et = fd_curr_date,
fd_curr_time, ld_curr_date,
ld_curr_time
samples = ['360Min', '300Min',
'240Min', '180Min', '120Min',
'60Min']

selection = '60Min'

if request.method == 'POST':
## iau datele din datetime
selector
start_date =
datetime.datetime.strptime(request.fo
rm['start_date'], '%Y -%m-
%dT%H:%M:%S')
end_date =
datetime.datetime.strptime(request.fo
rm['end_date'], '%Y -%m-%dT%H:%M:%S')
selection =
request.form['slct']
## parsez date le din datetime
selector
sd = start_date.strftime("%Y –
%m-%d")
st =
start_date.strftime("%H:%M:%S")
ed = end_date.strftime("%Y –
%m-%d")
et =
end_date.strftime("%H:%M:%S")
diff, samples =
suggest_sampling_time_interval( 'rpi_d
ata_1', sd, st, ed, et)

ld_curr_date = ed
ld_curr_time = et

61
fd_curr_date = sd
fd_curr_time = st

return
render_template("rpi_dev_two.html",
fd_curr_date=fd_curr_date,
fd_curr_time=fd_curr_time,
ld_curr_date=ld_cur r_date,
ld_curr_time=ld_curr_time,
start_date=sd, start_time=st,
end_date=ed, end_time=et,
spl=map(json.dumps, samples),
selection=selection)

@app.route('/compared_data',
methods=['GET', 'POST'])
def compared_data():
ld_curr_date, ld_curr_time,
fd_curr_date, fd_curr_time =
get_timestamps('rpi_data_1')
ld_curr_time, fd_curr_time =
ld_curr_time.strftime("%H:%M:%S"),
fd_curr_time.strftime("%H:%M:%S")

sd, st, ed, et = fd_curr_date,
fd_curr_time, ld_curr_date,
ld_curr_time
samples = ['360Min', '300Min',
'240Min', '180Min', '120Min',
'60Min']

selection = '60Min'

if request.method == 'POST':
## iau datele din datetime
selector
start_date =
datetime.datetime.strptime(request.fo
rm['start_date'], '%Y -%m-
%dT%H:%M:%S')
end_date =
datetime.datetime.strptime(request.fo
rm['end_date'], '%Y -%m-%dT%H:%M:%S')
selection =
request.form['slct']
## parsez datele din datetime
selector
sd = start_date.strftime("%Y –
%m-%d")
st =
start_date.strftime("%H :%M:%S")
ed = end_date.strftime("%Y –
%m-%d")
et =
end_date.strftime("%H:%M:%S")
diff, samples =
suggest_sampling_time_interval('rpi_d
ata_1', sd, st, ed, et)

ld_curr_date = ed ld_curr_time = et
fd_curr_date = sd
fd_curr_time = st

return
render_template("compared_data.html",
fd_curr_date=fd_curr_date,
fd_curr_time=fd_curr_time,
ld_curr_date=ld_curr_date,
ld_curr_time=ld_curr_time,
start_date=sd, start_time=st,
end_date=ed, end_time=et,
spl=map(json. dumps, samples),
selection=selection)

### ex of calling the route with
custom paramaters:
##
http://192.168.192.154:5000/plot/temp
erature?device_name=rpi_1&samples=200
@app.route('/plot/temperature',
methods=['GET'])
def plot_temperature():
## retri eving the data from
database // latest 10 values
temps, hums, gas_level, _,
current_dates, current_times, _ =
get_data_bulk_table(request.args.get(
'table_name'), 10,
request.args.get('device_name'))
temps, hums, gas =
verify_data(temps, hums, gas_l evel)
### creating the graphic
y_axis = temps
_figure = Figure(figsize=(5.16,
3.64))
axis = _figure.add_subplot(1, 1,
1)
axis.set_title("Temperature
[*C]")
axis.set_xlabel("Samples")
axis.grid(True)
xs = range(10)
axis.set_ylim([(min(temps) –
1),(max(temps)+1)])
axis.plot(xs, y_axis, " -xr")
for i, txt in enumerate(temps):
axis.annotate(txt, (i,
temps[i]), xytext=(5,10), ha='right',
textcoords='offset points')
canvas = FigureCanvas(_figure)
output = i o.BytesIO()
canvas.print_png(output)
response =
make_response(output.getvalue())
response.mimetype = 'image/png'
return response
## return
request.args.get('device_name')

62

@app.route('/plot/humidity',
methods=['GET'])
def plot_humidity( ):
## retrieving the data from
database // latest 10 values
temps, hums, gas_level, _,
current_dates, current_times, _ =
get_data_bulk_table(request.args.get(
'table_name'), 10,
request.args.get('device_name'))
temps, hums, gas =
verify_data(tem ps, hums, gas_level)
## creating the graphic
y_axis = hums
_figure = Figure(figsize=(5.16,
3.64))
axis = _figure.add_subplot(1, 1,
1)
axis.set_title("Humidity [%]")
axis.set_xlabel("Samples")
axis.grid(True)
axis.set_ylim([( min(hums) –
1),(max(hums)+1)])
xs = range(10)
axis.plot(xs, y_axis, " -xg")
for i, txt in enumerate(hums):
axis.annotate(txt, (i,
hums[i]), xytext=(5,10), ha='right',
textcoords='offset points')
canvas = FigureCanvas(_figure)
output = io.BytesIO()
canvas.print_png(output)
response =
make_response(output.getvalue())
response.mimetype = 'image/png'
return response

@app.route('/plot/gas_level',
methods=['GET'])
def plot_gas_level():
## retrieving the data from
database
_, _, gas_level, _, _, _, _ =
get_data_bulk_table(request.args.get(
'table_name'), 10,
request.args.get('device_name'))
_, _, gas = verify_data(_, _,
gas_level)
## creating the graphic
y_axis = gas
_figure = Figure(figsize=(5.16,
3.64))
axis = _figure.add_subplot(1, 1,
1)
axis.set_title("Gas Level [ppm]")
axis.set_xlabel("Samples")
axis.grid(True)
xs = range(10) axis.set_ylim([(min(gas) –
1),(max(gas)+1)])
axis.plot(xs, y_axis, " -xb")
for i, txt in enum erate(gas):
axis.annotate(txt, (i,
gas[i]), xytext=(5,10), ha='right',
textcoords='offset points')
canvas = FigureCanvas(_figure)
output = io.BytesIO()
canvas.print_png(output)
response =
make_response(output.getvalue())
response.mimetype = 'image/png'
return response

@app.route('/plot/pressure',
methods=['GET'])
def plot_pressure():
## retrieving the data from
database
_, _, _, pressure , _, _, _ =
get_data_bulk_table(request.args.get(
'table_name'), 10,
request.arg s.get('device_name'))
##_, _, gas = verify_data(_, _,
gas_level)
## creating the graphic
y_axis = pressure
_figure = Figure(figsize=(5.16,
3.64))
axis = _figure.add_subplot(1, 1,
1)
axis.set_title("Pressure [mmHg]")
axis.set_xla bel("Samples")
axis.grid(True)
xs = range(10)
axis.set_ylim([(min(pressure) –
1),(max(pressure)+1)])
axis.plot(xs, y_axis, " -xy")
for i, txt in
enumerate(pressure):
axis.annotate(txt, (i,
pressure[i]), xytext=(5,10),
ha='right', t extcoords='offset
points')
canvas = FigureCanvas(_figure)
output = io.BytesIO()
canvas.print_png(output)
response =
make_response(output.getvalue())
response.mimetype = 'image/png'
return response

@app.route('/plot/time_interval/c ombi
ned_data', methods=['GET'])
def
plot_timeinterval_combined_data():

sd = request.args.get('sd')

63
st = request.args.get('st')
ed = request.args.get('ed')
et = request.args.get('et')
selection =
request.args.get('selection')

dev_one = 'rpi_1'
dev_two = 'rpi_2'

table_one = 'rpi_data_1'
table_two = 'rpi_data_2'

elements_dev_one =
generate_aggregate_series_by_time(tab
le_one, 300, dev_one, selection, sd,
st, ed, et)
elements_dev_two =
generate_aggregate_serie s_by_time(tab
le_two, 300, dev_two, selection, sd,
st, ed, et)

temps_dev_one =
[x["temperature_avg"] for x in
elements_dev_one]
hums_dev_one = [x["humidity_avg"]
for x in elements_dev_one]
gas_dev_one = [x["gas_level_avg"]
for x in elements_de v_one]
pres_dev_one =[x["pressure_avg"]
for x in elements_dev_one]
dates_dev_one = [x["datetime"]
for x in elements_dev_one]

temps_dev_two =
[x["temperature_avg"] for x in
elements_dev_two]
hums_dev_two = [x["humidity_avg"]
for x in elemen ts_dev_two]
gas_dev_two = [x["gas_level_avg"]
for x in elements_dev_two]
pres_dev_two = [x["pressure_avg"]
for x in elements_dev_two]
dates_dev_two = [x["datetime"]
for x in elements_dev_two]

_figure = Figure(figsize=(19,
13.5), facecolor= "#c6dcff")

axis_temp =
_figure.add_subplot(4, 1, 1)
axis_hum = _figure.add_subplot(4,
1, 2)
axis_gas = _figure.add_subplot(4,
1, 3)
axis_pres =
_figure.add_subplot(4, 1, 4)

# axis_temp_dev_two =
_figure.add_subplot(3, 1, 1)
# axis_h um_dev_two =
_figure.add_subplot(3, 1, 2) # axis_gas_dev_two =
_figure.add_subplot(3, 1, 3)

axis_temp_dev_two =
axis_temp.twiny()
axis_hum_dev_two =
axis_hum.twiny()
axis_gas_dev_two =
axis_gas.twiny()
axis_pres_dev_two =
axis_pres.twin y()

_figure.tight_layout()

_figure.subplots_adjust(hspace=1.2,
bottom=0.125, top=0.9) ## adjust the
space between plots
## start working on axis settings

y_axis_temp = temps_dev_one
y_axis_hum = hums_dev_one
y_axis_gas = gas_dev_o ne
y_axis_pres = pres_dev_one

y_axis_temp_dev_two =
temps_dev_two
y_axis_hum_dev_two = hums_dev_two
y_axis_gas_dev_two = gas_dev_two
y_axis_pres_dev_two =
pres_dev_two

axis_temp.set_title("temperature
by datetime")
axis_hum.set_title("humidity by
datetime")
axis_gas.set_title("gas level by
datetime")
axis_pres.set_title("atmospheric
pressure")

axis_temp.set_xlabel("timestamps")
axis_hum.set_xlabel("timestamps")
axis_gas.set_xlabel("timestamps")

axis_pres.set_xlabel("timestamps")

axis_temp.grid(True)
axis_hum.grid(True)
axis_gas.grid(True)
axis_pres.grid(True)

x_format =
matplotlib.dates.DateFormatter('%Y –
%m-%d %H:%M:%S')

axis_temp.xaxis.set_major_formatter(x
_format)

64

axis_hum.xaxis.set_major_formatter(x_
format)

axis_gas.xaxis.set_major_formatter(x_
format)

axis_pres.xaxis.set_major_formatter(x
_format)

axis_temp.set_xticks(np.arange(len(da
tes_dev_one)))

axis_hum.set_xticks(np.arange(len( dat
es_dev_one)))

axis_gas.set_xticks(np.arange(len(dat
es_dev_one)))

axis_pres.set_xticks(np.arange(len(da
tes_dev_one)))

axis_temp_dev_two.set_xticks(np.arang
e(len(dates_dev_two)))

axis_hum_dev_two.set_xticks(np.arange
(len(dates_dev_two)))

axis_gas_dev_two.set_xticks(np.arange
(len(dates_dev_two)))

axis_pres_dev_two.set_xticks(np.arang
e(len(dates_dev_two)))

axis_temp.set_xticklabels(dates_dev_o
ne, rotation=20)

axis_hum.set_xticklabels(dates_dev_on
e, rotation=20)

axis_gas.set_xticklabels(dates_dev_on
e, rotation=20)

axis_pres.set_xticklabels(dates_dev_o
ne, rotation=20)

axis_temp_dev_two.set_xticklabels(dat
es_dev_two, rotation=20)

axis_hum_dev_two.set_xticklabels(date
s_dev_two, rotation=20)

axis_gas_dev_t wo.set_xticklabels(date
s_dev_two, rotation=20)

axis_pres_dev_two.set_xticklabels(dat
es_dev_two, rotation=20)

axis_temp.set_ylim([(min(temps_dev_on
e)-3),(max(temps_dev_one)+3)])

axis_hum.set_ylim([(min(hums_dev_one)
-10),(max(hums_dev_one)+1 0)])

axis_gas.set_ylim([(min(gas_dev_one) –
50),(max(gas_dev_one)+50)])

axis_pres.set_ylim([(min(gas_dev_one)
-50),(max(gas_dev_one)+50)])

axis_temp_dev_two.set_ylim([(min(temp
s_dev_two) –
3),(max(temps_dev_two)+3)])

axis_hum_dev_two.set_ylim([ (min(hums_
dev_two) -10),(max(hums_dev_two)+10)])

axis_gas_dev_two.set_ylim([(min(gas_d
ev_two)-50),(max(gas_dev_two)+50)])

axis_pres_dev_two.set_ylim([(min(pres
_dev_two) –
50),(max(pres_dev_two)+50)])

axis_temp.plot(np.arange(len(dates_de
v_one)), y_axis_temp, ' -or',
label='temperature rpi_dev_1')

axis_temp_dev_two.plot(np.arange(len(
dates_dev_two)), y_axis_temp_dev_two,
'-Xb', label='temperature rpi_dev_2')

axis_hum.plot(np.arange(len(dates_dev
_one)), y_axis_hum, ' -og',
label='humidity rpi _dev_1')

axis_hum_dev_two.plot(np.arange(len(d
ates_dev_two)), y_axis_hum_dev_two,
'-Xr', label='humidity rpi_dev_2')

axis_gas.plot(np.arange(len(dates_dev
_one)), y_axis_gas, ' -ob',
label='gas_level rpi_dev_1')

axis_gas_dev_two.plot(np.arange(l en(d
ates_dev_two)), y_axis_gas_dev_two,
'-Xg', label='gas_level rpi_dev_2')

axis_pres.plot(np.arange(len(dates_de
v_one)), y_axis_pres, ' -oy',
label='atmospheric pressure
rpi_dev_1')

65

axis_pres_dev_two.plot(np.arange(len(
dates_dev_two)), y_axis_pres _dev_two,
'-Xr', label='atmospheric pressure
rpi_dev_2')

axis_temp.legend()
axis_hum.legend()
axis_gas.legend()
axis_pres.legend()

axis_temp_dev_two.legend(loc=4)
axis_hum_dev_two.legend(loc=4)
axis_gas_dev_two.legend(loc=4)
axis_pres_dev_two.legend(loc=4)

## here I render it for returning
it as response to route call
canvas = FigureCanvas(_figure)
output = io.BytesIO()
canvas.print_png(output)
response =
make_response(output.getvalue())
response.m imetype = 'image/png'
return response

@app.route('/plot/time_interval/ data'
, methods=['GET'])
def plot_timeinterval_data():
table =
request.args.get('table_name')
dev =
request.args.get('device_name')
sd = request.args.get('sd')
st = request.args.get('st')
ed = request.args.get('ed')
et = request.args.get('et')
selection =
request.args.get('selection')
print table, dev
elements =
generate_aggregate_series_by_time(tab
le, 300, dev, selection, sd, st, ed,
et)
print len(elements) ## 31
currently
temps = [x["temperature_avg"] for
x in elements]
hums = [x["humidity_avg"] for x
in elements]
gas = [x["gas_level_avg"] for x
in elements]
pres = [x["pressure_avg"] for x
in elements]
dates = [x["datetime" ] for x in
elements]

_figure = Figure(figsize=(19,
11.5), facecolor="#c6dcff")
axis_temp =
_figure.add_subplot(4, 1, 1)
axis_hum = _figure.add_subplot(4,
1, 2)
axis_gas = _figure.add_subplot(4,
1, 3)
axis_pres =
_figure.add_subplot(4, 1, 4)

_figure.tight_layout()

_figure.subplots_adjust(hspace=0.9,
bottom=0.125) ## adjust the space
between plots
## start working on axis settings

y_axis_temp = temps
y_axis_hum = hums
y_axis_gas = gas
y_axis_pres = pres
for i in range(1):
print "y_axis_pres: "
print y_axis_pres[i]

axis_temp.set_title("temperature
by datetime")
axis_hum.set_title("humidity by
datetime")
axis_gas.set_title("gas level by
datetime")
axis_pres.set_title("pr essure
level by datetime")

axis_temp.set_xlabel("timestamps")
axis_hum.set_xlabel("timestamps")
axis_gas.set_xlabel("timestamps")

axis_pres.set_xlabel("timestamps")

axis_temp.grid(True)
axis_hum.grid(True)
axis_gas.gri d(True)
axis_pres.grid(True)

x_format =
matplotlib.dates.DateFormatter('%Y –
%m-%d %H:%M:%S')

axis_temp.xaxis.set_major_formatter(x
_format)

axis_hum.xaxis.set_major_formatter(x_
format)

axis_gas.xaxis.set_major_formatter(x_
format)

66

axis_pres.xaxis.set_major_formatter(x
_format)

axis_temp.set_xticks(np.arange(len(da
tes)))

axis_hum.set_xticks(np.arange(len(dat
es)))

axis_gas.set_xticks(np.arange(len(dat
es)))

axis_pres.set_xticks(np.arange(len(da
tes)))

axis_temp.set_xticklabels(dates,
rotation=20)
axis_hum.set_xticklabels(dates,
rotation=20)
axis_gas.set_xticklabels(dates,
rotation=20)
axis_pres.set_xticklabels(dates,
rotation=20)

axis_temp.set_ylim([(min(temps) –
3),(max(temps)+3)])
axis_hum.set_ylim([(min(hums) –
10),(max(hums)+10)])
axis_gas.set_ylim([(min(gas) –
50),(max(gas)+50)])
axis_pres.set_ylim([(min(pres) –
10),(max(pres)+10)])

axis_temp.plot(np.arange(len(dates)),
y_axis_temp, ' -or',
label='temperature')

axis_hum.plot(np.arange(len(dates)),
y_axis_hum, ' -og', label='humidity')

axis_gas.plot(np.arange(len(dates)),
y_axis_gas, ' -ob', label='gas_level')

axis_pres.plot(np.arange(len(dates)),
y_axis_pres, ' -oy',
label='atmospheric pressure')

for i, txt in enumerate(temps):

axis_temp.annotate(round(txt,2), (i,
temps[i]), xytext=(5,10), ha='right',
textcoords='offset points')
for i, txt in enumerate(hums):
axis_hum.annotate(round(txt,
2), (i, hums[i]), xytext=(5,10), ha='right', tex tcoords='offset
points')

for i, txt in enumerate(gas):
axis_gas.annotate(round(txt,
2), (i, gas[i]), xytext=(5,10),
ha='right', textcoords='offset
points')

for i, txt in enumerate(pres):
axis_pres.annotate(round(txt,
2), (i, pres[ i]), xytext=(5,10),
ha='right', textcoords='offset
points')

axis_temp.legend()
axis_hum.legend()
axis_gas.legend()
axis_pres.legend()

## here I render it for returning
it as response to route call
canvas = FigureCanvas(_figure)
output = io.BytesIO()
canvas.print_png(output)
response =
make_response(output.getvalue())
response.mimetype = 'image/png'
return response

## MUST BE DONE AS API RESOURCE
@app.route('/ping')
def status():
return jsonify({
'message:': 'pong',
'status': 'success'
})

api.add_resource(DeviceAPI,
'/api/devices')

if __name__ == "__main__":
## this is just a test so i can test
generate_random_values() function
## temperature, humidity =
generate_random_values()
## print("Temp: {0:.2f}*C \tHum:
{1:.2f}%".format(temperature,
humidity))
app.secret_key = os.urandom(12)
app.run(host='0.0.0.0',
debug=True)

67
Anexa 4
import random
import sqlite3
import psycopg2
import json
import os
import io
import datetime
import pandas as pd

from psycopg2.extensions import AsIs
from flask import Flask, jsonify,
render_template, redirect, flash,
request, session, abort,
make_response
from flask_restful import Resource,
Api
from matplotlib.backends.backend_agg
import Figur eCanvasAgg as
FigureCanvas
from matplotlib.figure import Figure

# create a list of dict(json) objects
with data from `devices` table
(CORRECT)
def create_devices_dict(function,
args):
values = function(*args)
elements = []
for element in valu es:
el_as_json = {
'device_id':
(int)(str(element["device_id"])),
'device_name':
str(element["device_name"]),
'status':
str(element["status"])
}
elements.append(el_as_json)
return elements

## return me the list of all devices
in the system; (CORRECT)
def get_devices_list():
with
psycopg2.connect(host='192.168.100.11
0', port='5432', dbname='data -db',
user='postgres', password='delta123')
as conn:
curs = conn.cursor( )
curs.execute("""SELECT * FROM
devices;""")
data = curs.fetchall()
curs.execute("""SELECT
column_name FROM
information_schema.columns where table_schema='public' and
table_name='devices'""")
col_description = [row[0] for
row in curs]
## values = create_list(data,
col_description)
elements =
create_devices_dict(create_list,
(data, col_description))
conn.close()
return elements

## create a list of dicts (CORRECT)
def create_list(data,
col_description):
values = [value for value in
data]
properties = [x for x in
col_description]
elements = []
for value in values:
element = {}
for prop, v in
zip(properties, value):
element[prop] = v
elements.append(element)
return elements

## format the retrieved reading
samples; (CORRECT)
def create_data_dict(function, args):
values = function(*args)
elements = []
for element in values:
el_as_json = {
'data_id':
(int)(str(element["data_ id"])),
'temperature':
round((float)(str(element["temperatur
e"])), 2),
'humidity':
round((float)(str(element["humidity"]
)), 2),
'gas_level':
round((float)(str(element["gas_level"
]))),
'pressur e':
round((float)(str(element["pressure"]
))),
'curr_date':
element["curr_date"].strftime("%Y -%m-
%d"),
'curr_time':
element["curr_time"].strftime("%H:%M:
%S"),

68
'device_name':
str(element["device_name"])
}
elements.append(el_as_json)
## print elements
return elements

## return me a number of samples, for
a selected device (CORRECT)
def get_db_entries(table, device,
samples):
with
psycopg2.connect(host='192.168.100.11
0', port='5432 ', dbname='data -db',
user='postgres', password='delta123')
as conn:
curs = conn.cursor()
curs.execute("""SELECT * FROM
%s WHERE device_name=%s ORDER BY
(curr_date, curr_time) DESC LIMIT
%s""", (AsIs(table),
device,samples,))
data = curs.fetchall()
curs.execute("""SELECT
column_name FROM
information_schema.columns where
table_schema='public' and
table_name='%s'""", (AsIs(table),))
col_description = [row[0] for
row in curs]
## print data
## print col_descr iption
## print "columns:
{}".format(col_description)
## values = create_list(data,
col_description)
elements =
create_data_dict(create_list, (data,
col_description))
conn.close()
return elements

## currently not used
def get_db_last_e ntry():
with
sqlite3.connect('sensordata.db') as
conn:
curs = conn.cursor()
last_entry =
curs.execute('SELECT * FROM
sensorreadings ORDER BY current_time
DESC LIMIT 1').fetchone()
temperature, humidity,
current_date, current_tim e, device =
last_entry[1], last_entry[2],
last_entry[3], last_entry[4],
last_entry[5]
print 'Closing the connection to
database'
conn.close() print 'Connection closed'
return temperature, humidity,
current_date, current_time, device

## create a dictionary of JSON
objects from objects returned by a
query
#def create_list_dict(query):
# values = [value for value in
query]
# properties = [x[0] for x in
query.description]
# elements = []
# for value in values:
# element = {}
# for prop, v in
zip(properties, value):
# element[prop] = v
# elements.append(element)
# return elements

def get_timestamps(table):
with
psycopg2.connect(host='192.168.100.11
0', port='5432', dbname='data -db',
user='postg res', password='delta123')
as conn:
curs = conn.cursor()
curs.execute("""SELECT * FROM
%s ORDER BY (curr_date, curr_time)
DESC LIMIT 1""", (AsIs(table),))
first_data = curs.fetchall()
curs.execute("""SELECT * FROM
%s ORDER B Y (curr_date, curr_time)
ASC LIMIT 1""", (AsIs(table),))
second_data = curs.fetchall()
conn.close()
ld_curr_date, ld_curr_time =
first_data[0][5], first_data[0][6]
fd_curr_date, fd_curr_time =
second_data[0][5], second_data[0][6]
return ld_curr_date,
ld_curr_time, fd_curr_date,
fd_curr_time

def get_data_bulk(samples,
device_name):
with
psycopg2.connect(host='192.168.100.11
0', port='5432', dbname='data -db',
user='postgres', password='delta123')
as conn:
curs = conn.curso r()
curs.execute("""SELECT * FROM
sensor_readings WHERE device_name=%s
ORDER BY (curr_date, curr_time) DESC
LIMIT %s""", (device_name,samples,))
data = curs.fetchall()

69
curs.execute("""SELECT
column_name FROM
information_schema.colum ns where
table_schema='public' and
table_name='sensor_readings'""")
col_description = [row[0] for
row in curs]
elements =
create_data_dict(create_list, (data,
col_description))
conn.close()
temps, hums, gas_level, pressure,
curr_dates, curr_times = ([] for i in
range(6))
for element in elements:

temps.append(element["temperature"])

hums.append(element["humidity"])

gas_level.append(element["gas_level"]
)

pressure.append(element["pressure"])

curr_dates.append(element["curr_date"
])

curr_times.append(element["curr_time"
])
return temps, hums, gas_level,
pressure, curr_dates, curr_times

def get_data_bulk_table(table,
samples, device_name):
with
psycopg2.connect(host='192.168.100.11
0', port='5432', dbname='data -db',
user='postgres', password='delta123')
as conn:
curs = conn.cursor()
curs.execute("""SELECT * FROM
%s WHERE device_name=%s ORDER BY
(curr_date, curr_time) DESC LIMIT
%s""",
(AsIs(table),device_name,samples,))
data = curs.fetchall()
curs.execute("""SELECT
column_name FROM
information_schema.columns where
table_schema='public' and
table_name='%s'""", (AsIs(table),))
col_description = [row[0] for
row in curs]
elements =
create_data_dict (create_list, (data,
col_description))
conn.close() ids, temps, hums, gas_level,
pressure, curr_dates, curr_times =
([] for i in range(7))
for element in elements:

ids.append(element["data_id"])

temps.append(element["temperature "])

hums.append(element["humidity"])

gas_level.append(element["gas_level"]
)

pressure.append(element["pressure"])

curr_dates.append(element["curr_date"
])

curr_times.append(element["curr_time"
])
return temps, hums, gas_level,
pressure, curr_dates, curr_times,
elements

def
suggest_sampling_time_interval(table,
start_date, start_time, end_date,
end_time):
with
psycopg2.connect(host='192.168.100.11
0', port='5432', dbname='data -db',
user='postgres', password='delt a123')
as conn:
curs = conn.cursor()
curs.execute("""SELECT * FROM
%s WHERE (curr_date = %s) AND
(curr_time >= %s) ORDER BY
(curr_date, curr_time) ASC LIMIT
1""", (AsIs(table), start_date,
start_time,))
data = curs.fetchall()
start_id = data[0][0]
curs.execute("""SELECT * FROM
%s WHERE (curr_date = %s) AND
(curr_time <= %s) ORDER BY
(curr_date, curr_time) DESC LIMIT
1""", (AsIs(table), end_date,
end_time,))
data = curs.fetchall()
end_id = data[0][0]
diff = end_id – start_id
conn.close()

avg = (int)(diff/30)
samples = ['360Min', '300Min',
'240Min', '180Min', '120Min',
'60Min']

70
if avg in range(48, 96): ##de la
2 zile pana la 1 zi
samples = ['120Min', '90Min',
'60Min']
elif avg in range(24, 48): ##de
la 1 zi pana la 1/2 zi
samples = ['120Min', '60Min',
'45Min', '30Min']
elif avg in range(12, 24): ## de
la 12h la 6h
samples = ['60Min', '45Min',
'30Min', '15Min']
elif avg in range(6, 12): ## de
la 6h pana la 3h
samples = ['60Min', '45Min',
'30Min', '15Min']
elif avg in range(3, 6): ## de la
3h pana la 1.5h
samples = ['45Min', '30Min',
'15Min', '10Min', '5Min']
elif avg in range(1, 3): ## de la
30min pana la 1.5h
samples = ['10Min', '5Min',
'3Min']
elif ((int)(diff/1)) in range(1,
30):
samples = ['1Min']
else:
samples = ['360Min',
'300Min', '240Min', '120Min']

return diff, samples

def get_range_data_bulk_table(table,
start_date, start_time, en d_date,
end_time):
with
psycopg2.connect(host='192.168.100.11
0', port='5432', dbname='data -db',
user='postgres', password='delta123')
as conn:
curs = conn.cursor()
curs.execute("""SELECT * FROM
%s WHERE (curr_date = %s) AND
(curr_time > = %s) ORDER BY
(curr_date, curr_time) ASC LIMIT
1""", (AsIs(table), start_date,
start_time,))
data = curs.fetchall()
start_id = data[0][0]
curs.execute("""SELECT * FROM
%s WHERE (curr_date = %s) AND
(curr_time <= %s) ORDER BY
(curr_date, curr_time) DESC LIMIT
1""", (AsIs(table), end_date,
end_time,))
data = curs.fetchall()
end_id = data[0][0]
curs.execute("""SELECT * FROM
%s WHERE (data_id >= %s AND data_id <= %s) ORDER BY (curr_date,
curr_time) DESC""", (AsIs (table),
start_id, end_id))
data = curs.fetchall()
curs.execute("""SELECT
column_name FROM
information_schema.columns where
table_schema='public' and
table_name='%s'""", (AsIs(table),))
col_description = [row[0] for
row in curs]
diff = end_id – start_id
elements =
create_data_dict(create_list, (data,
col_description))
conn.close()
ids, temps, hums, gas_level,
pressure, curr_dates, curr_times =
([] for i in range(7))
for element in elements:

ids.append( element["data_id"])

temps.append(element["temperature"])

hums.append(element["humidity"])

gas_level.append(element["gas_level"]
)

pressure.append(element["pressure"])

curr_dates.append(element["curr_date"
])

curr_times.append(element["curr_time"
])
for i in range(1,5):
print elements[i]
return temps, hums, gas_level,
pressure, curr_dates, curr_times,
elements, diff

def generate_timestamp(date, time):
n_date =
datetime.datetime.strptime(date, '%Y-
%m-%d').date()
n_time =
datetime.datetime.strptime(time,
'%H:%M:%S').time()
return n_date, n_time

def get_frequency_sample(table_name,
device_name):
temp, hum, gas_level, curr_date,
curr_time, _ =
get_data_bulk_table(table_name, 2,
device_name)
date_format = '%Y -%m-%d %H:%M:%S'

71
n_date, n_time =
generate_timestamp(curr_date[1],
curr_time[1])
timestamp_one =
datetime.datetime.combine(n_date,
n_time)
n_date, n_time =
generate_timestamp(curr_date[0],
curr_time[0])
timestam p_two =
datetime.datetime.combine(n_date,
n_time)
_freq = timestamp_two –
timestamp_one
_freq =
int(round(_freq.total_seconds()/60))
return _freq

def verify_data(temp_array,
hum_array, gas_array):
_len = len(temp_array)
for i in range (0, _len -1):
if(temp_array[i] < -10 or
temp_array[i] > 50):
temp_array[i] =
temp_array[i -2]
if(hum_array[i] < 0 or
hum_array[i] > 100):
hum_array[i] =
hum_array[i -2]
if(gas_array[i] < 0 or
gas_array[i] > 1000 ):
gas_array[i] =
gas_aray[i -2]
return temp_array, hum_array,
gas_array

def
generate_aggregate_series_by_time(tab
le, samples, device_name,
time_interval, start_date,
start_time, end_date, end_time):
##_, _, _, _, _, elements =
get_data_bulk_table(table, samples,
device_name)
_, _, _, _, _, _, elements, diff
= get_range_data_bulk_table(table,
start_date, start_time, end_date,
end_time)
df = pd.DataFrame(elements)
df['datetime'] =
df.curr_date.astype(str) + ' ' +
df.curr_time .astype(str)
del df['curr_date'],
df['curr_time'], df['data_id']
df.datetime =
pd.to_datetime(df.datetime)
df.set_index('datetime',
inplace=True)
grpby_time_df =
df.groupby(pd.TimeGrouper(time_interv
al))['temperature', 'humidity',
'gas_leve l',
'pressure'].mean().add_suffix('_avg')
.reset_index()
grpby_time_df['device_name'] =
device_name
final_list =
grpby_time_df.T.to_dict().values()
return final_list

72

73
Anexa 5
#!/usr/bin/python

import spidev
import time
import datetime
import psycopg2
import logging
import Adafruit_DHT
import Adafruit_BMP.BMP085 as BMP085
import socket

from time import sleep
from psycopg2.extensions import AsIs
from systemd.journal import
JournaldLogHandler
from flask import F lask, jsonify
from multiprocessing import Process

app = Flask(__name__)

##configure logger
logging.basicConfig(level=logging.INF
O)
logger = logging.getLogger(__name__)
logger.propagate = False
##configure filehandler
handler =
logging.FileHandler('/hom e/pi/app.log
')
##configure systemd handler
journald_handler =
JournaldLogHandler()
journald_handler.setFormatter(logging
.Formatter('[%(levelname)s]
%(message)s'))
##set logging level
handler.setLevel(logging.INFO)
##configure the logging format
formatter =
logging.Formatter('%(asctime)s –
%(name)s – %(levelname)s –
%(message)s')
handler.setFormatter(formatter)
##add the handler to the logger
logger.addHandler(handler)
logger.addHandler(journald_handler)

@app.route('/api/ping',
methods=['GET'])
def ping():
return jsonify({'message':
'pong'})

def insert_data(device_name, table,
temperature, humidity, gas_level,
pressure):
now = datetime.datetime.now()
curr_date = now.strftime("%Y -%m-
%d")
curr_time =
now.strftime("%H:%M:%S")

print "open connection to
database"
logger.info("Open connection to
database")
with
psycopg2.connect(host="192.168.100.11
0", port="5432", dbname="data -db",
user="postgres", password="delta123")
as conn:
curs = conn.cursor()
curs.execute("INSER T INTO %s
(temperature, humidity, gas_level,
pressure, curr_date, curr_time,
device_name) VALUES (%s, %s, %s, %s,
%s, %s, %s);", (AsIs(table),
temperature, humidity, gas_level,
pressure, curr_date, curr_time,
device_name,))
conn.commit()
print "closing the connection"
logger.info("Closing connection
to database")
conn.close()

def read_analog_data(device = 0,
channel = 0):
## ma asigur ca device/channel
pot sa ia doar 2 valori -> 0 si 1
assert device in (1, 0)
assert channel in (1, 0)
#instantiez obiectul de tip SPI
spi = spidev.SpiDev()
#deschid SPI -ul
spi.open(0, device)
spi.max_speed_hz = 1000000

"""
protocol: SB(start bit) [S],
SGL/DIFF [D], ODD/SIGN [C], MSBF [M]
se folosesc zerouri de in ceput pt
un ciclu de ceas mai stabil
eg. 0000 000S DCM0 0000 0000 0000
prin trimiterea a 3 pachete a
cate 8 biti rezultatul va fi
reprezentat
tot din 3 pachete
"""
command = [1, (2 + channel) << 6,
0]

74
"""
avem 2 canale pe MCP300 2: CH0,
CH1 repr binar: 10 sau 11
10 << 6 = 1000 0000
11 << 6 = 1100 0000
"""

reply = spi.xfer2(command) ##
sau: reply = spi.xfer2([1, (2 +
channel) << 6, 0])
value = reply[1] & 31 ## reply
este rezultatul obtinut de la iesirea
portului: Dout
value = value << 6
value = value + (reply[2] >> 2)
spi.close()
return value

def read_pressure():
sensor = BMP085.BMP085()
pressure =
(sensor.read_pressure() / 133.322365)
return pressure

def read_smoke_level():
smoke_level = read_analog_data(0,
0) ## aici avem legat MCP3002
return smoke_level

def read_dht_sensor(sensor_type,
pin):
humidity, temperature =
Adafruit_DHT.read_retry(sensor_type,
pin)
return temperature, humidity

def query_data(sensor_ty pe, pin):
try:
while True:
## read 1st values
gas_one =
read_smoke_level()
temp_one, hum_one =
read_dht_sensor(sensor_type, pin)
pres_one =
read_pressure()

logger.info('Records[1]: \ttemperature
: %s[C]\thumidity:
%s[procent] \tgas_level: %s
ppm\tpressure: %s mmHg' % (temp_one,
hum_one, gas_one, pres_one))
sleep(19.33)
## read 2nd values
gas_two =
read_smoke_level() temp_two, hum_two =
read_dht_sensor(sensor_type, pin)
pres_two =
read_pressure()

logger.info('Records[2]: \ttemperature
: %s[C]\thumidity:
%s[procent] \tgas_level: %s
ppm\tpressure: %s mmHg' % (temp_two,
hum_two, gas_two, pres_two))
sleep(19.33)
## read 3rd values
gas_three =
read_smoke_level()
temp_three, hum_three =
read_dht_sensor(sensor_type, pin)
pres_three =
read_pressure()

logger.info('Records[3]: \ttemperature
: %s[C]\thumidity:
%s[procent]\tgas_level: %s
ppm\tpressure: %s mmHg' %
(temp_three, hum_three, gas_three,
pres_three))
sleep(19.33)
"""
read_analog_data(0, 0)
este echivalent cu read_analog_data()
de ce? pentru ca valorile
default(s tandard) pentru parametrii
functiei read_analog_data
sunt [0, 0]
"""
## media aritmetica a
valorilor pentru o precizie mai buna
mean_gas_level = (gas_one
+ gas_two + gas_three) / 3
mean_temp = (te mp_one +
temp_two + temp_three) / 3
mean_hum = (hum_one +
hum_two + hum_three) / 3
mean_pres = (pres_one +
pres_two + pres_three) / 3

logger.info('Records[f]: \ttemperature
: %s[C]\thumidity:
%s[procent] \tgas_level: %s
ppm\tpressure: %s mmHg' % (mean_temp,
mean_hum, mean_gas_level, mean_pres))
## print("gas level:
{0}\ttemperature:
{1:0.1f}*C \thumidity:
{2:0.1f}%".format(mean_gas_level,
mean_temp, mean_hum))
insert_data('rpi_1',
'rpi_data_1', mean_te mp, mean_hum,
mean_gas_level, mean_pres)
logger.info('Peaceful
shutdown of reading program')

75
except KeyboardInterrupt: ##
reprezinta prinderea exceptiei
determinata de apasarea combinatiei
CTRL+C
## print "Closing reading…"
logger.info('Force close the
reading program via CTRL+C')

if __name__ == "__main__":
logger.info('Start the reading
program')
sensor_type = Adafruit_DHT.DHT22
pin = 4 ##reprezinta pinul GPIO4,
nu pinul fizic; pinul fizic pt GPIO4
fiind 7
## ma asigur ca mereu SPI -ul va
fi inchis mereu
logger.info('Start querying the
sensors and add data to DB')
p = Process(target=query_data,
args=(sensor_type,pin,))
p.start()
logger.info('Start webserver for
status check')
app.run(host='0 .0.0.0',
use_reloader=False)
p.join()

76

77
Anexa 6

Montajul electric al nodului 1 din sistem

78

Mon tajul ele ctric al nodului 2 din sistem

79
Anexa 7

Pagina c are afișează disponibilitatea dispozitivelor și ultimi parametrii măsurați

80

Graficele afișate în pagina utilizată pentru compararea datelor între dispozitive

81

Grafice care afi șează ultimele 10 măsurăt ori înregistrate de fiecare nod

Similar Posts