Functionalitatea Si Controlul Optim al Lifturilor
Cuprins
Capitolul 1 Introducere
1.1 Context
1.2 Descriere generală a capitolelor
1.3 Obiective și specificații
Capitolul 2 Studiu bibliografic
2.1 Liftul – o scurtă istorie
2.2 Sisteme de control a lifturilor
2.2.1 Controlul bazat pe dulapuri cu relee
2.2.2 Controlul liftului folosind PLC-uri
2.2.3 Controlul liftului folosind microcontrolere
2.3 Metode folosite pentru controlul sistemelor de lifturi
2.3.1 Controlul unui sistem de lifturi folosind logica Fuzzy
2.3.2 Controlul unui sistem de lifturi axat pe optimizarea coordonatorului și reducerii consumului de energie
2.3.3 Controlul unui sistem de lifturi bazat pe rețele neuronale
2.4 Lifturile astăzi
Capitolul 3 Analiză și proiectare
3.1 Analiză și concepte generale
3.1.1 Ce este un algoritm?
3.1.2 Ce reprezintă un sistem de control?
3.1.3 Limbajul de programare JAVA
3.1.4 Programarea orientată pe obiect în Java
3.1.5 Design pattern în Java
3.1.6 Rețele Petri
3.1.7 Noțiuni generale ale diagramelor UML
3.2 Proiectarea aplicației
3.2.1 Arhitectura aplicației
3.2.2 Diagrame UML
Capitolul 4 Implementarea aplicației
4.1 Crearea și configurarea aplicației în mediul de dezvoltare Eclipse
4.2 Dezvoltarea aplicației
4.2.1 Clase folosite și utilizarea lor
4.2.2 Modul de implementare a funcționării aplicației
4.2.3 Implementarea logicii de control
4.2.4 Modelarea aplicației folosind Rețele Petri de nivel înalt cu temporizare
Capitolul 5 Testare și validare
5.1 Testarea aplicației
5.2 Validarea rezultatelor
Capitolul 6 Concluzii
6.1 Concluzii finale
6.2 Contribuții personale
6.3 Posibile dezvoltări ulterioare
Bibliografie
Acronime
ANEXA
Introducere
Context
Trăim într-o lume în care tehnologia a ajuns să fie, în cea mai mare parte, principalul punct de sprijin al activităților și nevoilor noastre de zi cu zi, fiind într-un stagiu de continuă dezvoltare și perfecționare. Tendința omului este de a crea și a avea o viață cât mai ușoară depunând un efort cât mai mic, de aceea a încercat și încearcă întotdeauna să găsească și să construiască anumite lucruri pentru a-i veni în ajutor in diferite situații.
În ziua de azi, timpul este cea mai prețioasă resursă și dorim să facem cât mai multe lucruri într-un timp cât mai scurt, iar dacă este posibil să nu îl lăsăm să treacă fără folos, cum ar fi: așteptarea la o anumită coadă (într-un magazin, în trafic etc.), așteptarea autobuzului în stație, precum și așteptarea unui lift și timpul necesar ajungerii la destinația dorită (influențat în mare parte de numărul de opriri ale liftului).
Să luăm, de exemplu, zonele urbane mai mari unde, din ce în ce mai multe clădiri au cel puțin 4 etaje, iar disconfortul datorat folosirii scărilor pentru a ajunge la un anumit etaj este de foarte multe ori nesuportabil; de asemenea, în cazul persoanelor cu anumite probleme sau diferitelor situații în care este necesară urcarea sau coborârea unui obiect mare sau greoi de la un etaj la altul, cu siguranță scările nu sunt o soluție acceptabilă.
Probabil cu toții am avut ocazia de a vedea sau a folosi un lift (fie de persoane, fie industrial) cel puțin o dată, și am văzut beneficiile pe care le aduce. Încă de la primele lifturi care au fost construite, ideea lor a fost de ușura urcarea sau coborârea la o anumită altitudine (sau nivel în cazul clădirilor) a diferitelor obiecte, materialelor de construcție etc., cât și a persoanelor, și de a scuti sau a diminua pe cât mai mult posibil de efortul depus de om pentru acele operații. La început, aceste beneficii nu veneau fără un minim de efort depus, deoarece funcționalitatea lifturilor se baza pe forța umană sau animală și mai târziu pe forța aburului. Dar odată cu avansarea tehnologiei și inventării motorului electric, funcționalitatea liftului a devenit una mult mai bună și mai eficientă, fapt care a determinat răspândirea lor în întreaga lume și utilizarea lor in diferite scopuri.
Cu toate că lifturile ne oferă diferite avantaje, există anumite situații în care avem parte și de unele neplăceri, iar liftul devine o variantă opțională. De exemplu, când intrați într-o clădire și doriți să urcați la etajul 7 și nici un lift nu se află la parter, iar când chemați liftul sau urcați (coborâți) cu el, acesa se oprește aproape (dacă nu) la toate etajele intermediare, făcând acea călătorie mult mai lungă decat timpul necesar folosirii scărilor.
De aceea, în ultimii ani s-a dorit rezolvarea a cât mai multor probleme legate de funcționalitatea și, în special, controlul optim al lifturilor în funcție de scop, specificații și mediul în care vor funcționa. Așa că, în ziua de azi ne putem bucura de utilitatea și ajutorul pe care ni-l aduce nevoilor noastre de zi de zi.
Descriere generală a capitolelor
Lucrarea curentă conține un număr de 6 capitole, printre care se află si acest capitol în care se setează contextul lucrării, se descriu obiectivele propuse și specificațiile după care va fi proiectată aplicația.
În capitolul 2, se face o scurtă prezentare a istoriei și evoluției lifturilor de-a lungul anilor, prezentându-se câteva moduri uzuale de control al lifturilor, cât și diferiți algoritmi folosiți pentru controlul acestora. În final se prezintă nivelul la care au evoluat lifturile din ziua de azi .
O analiză și descriere a tehnologiilor și metodelor de dezvoltare folosite în această lucrare sunt prezentate în capitolul 3. De asemenea, este prezentată și arhitectura aplicației împreună cu diagramene UML folosite la implementarea aplicației.
În următoarea secțiune a lucrării, capitolul 4, este descris modul în care a fost implementată aplicația, incluzând pașii urmați pentru crearea proiectului în mediul de dezvoltare Eclipse și crearea claselor, descrierea utilizării și funcționării fiecărei clase folosite, modul de implementare și funcționare al aplicației și al logicii de control, cât și modelarea sistemului folosind rețele Petri de nivel înalt.
Urmează capitolul 5, care include testarea și validarea aplicației. Aici sunt descrise cazurile de funcționare ale aplicației, testarea fiecăruia în parte, și analizarea și validarea rezultatelor obținute.
Capitolul 6 este ultimul din această lucrare, unde am descris implicarea și contribuția mea, și posibilele îmbunătățiri și funcționalități ce pot fi adăugate ulterior.
Obiective și specificații
Lifturile sunt utilizate în diferite domenii și pentru anumite scopuri, de aceea, de fiecare dată când este nevoie de un lift într-o anumită situație, este nevoie ca mai întâi să se cunoască următoarele: pentru ce va fi folosit, mediul și condițiile în care va opera, specificațiile de funcționare și funcționalități, modul de operare. Pe baza acestor lucruri se pot determina arhitectura, planul de construcție și, cel mai important, algoritmul de control, fără de care lifturile nu ar mai funcționa așa cum le știm.
Momentan cele mai răspândite sisteme de lifturi de persoane sunt formate, în medie, din unul sau două ascensoare, fiind controlate independent sau centralizat (în cazul a cel puțin două ascensoare). Prin control independent ne referim la controlul unui lift fără a ține cont și de starea și acțiunile celorlalte lifturi (dacă sunt prezente), fiecare având asignat câte un controller care va lua toate deciziile independent. Prin cel centralizat ne referim la controlul unui sistem lifturi în care alegerea unui ascensor pentru o anumită acțiune este realizată de un controller central care preia comenzile de la întreg sistemul, și în funcție de algoritmul de comandă care a fost implementat, va asigna liftul cel mai convenabil pentru fiecare cerere în parte. După modul în care sunt controlate, pot să apară unele avantaje și dezavantaje, de exemplu: pentru cel independent, un avantaj ar fi o implementare mult mai simplă a algoritmului de control si costurile aferente echipamentelor mai mici, iar un dezavantaj în cazul a cel puțin două ascensoare este că pentru o cerere de urcare sau coborâre se pot chema ambele lifturi și doar unul va fi folosit, celălalt fiind chemat fără scop și implicit scăzând disponibilitatea lui; pentru controlul centralizat un avantaj este că disponibilitatea lifturilor va crește, fiind puse în funcțiune, aproape întotdeauna, doar atunci când este nevoie de ele pentru a transporta una sau mai multe persoane, iar un dezavantaj ar fi complexitatea si dificultatea ridicată a implementării algoritmului de control dar și costurile echipamentelor sunt de asemenea mai mari.
Chemarea unui lift se face prin apăsarea unui buton, care în general este plasat lângă lift, și care poate poate fi unul simplu sau unul care indică direcția pentru care a fost chemat. Folosirea unui simplu buton este o alegere rea, deoarece liftul va opri întotdeauna la acel nivel unde a fost apăsat butonul, indiferent de direcția în care dorește să meargă persoana care l-a apăsat. De aceea, cele mai multe sisteme de lifturi au asignate pentru fiecare ascensor în parte câte un panou de comandă exterior, care are două butoane de indicare a direcției pentru care este solicitat liftul, reducând astfel opririle neintenționate.
În această lucrare se urmărește crearea unui algoritm de control optim și eficient a unui sistem de lifturi format din două ascensoare, care vor fi controlate de trei controlere: unul pentru fiecare ascensor în parte, și unul central care va primi toate comenzile de la pasageri și va coordona celelalte controlere.
Pentru implementarea aplicației, s-au folosit următoarele specificații:
Implementarea aplicației se face conform principiilor unui sistem cu evenimente discrete
Aplicația conține două lifturi ce operează într-un mediu virtual cu 4 nivele + parter
Utilizarea unei logici Fuzzy de alegere a unui lift pentru deservirea unei comenzi
Folosirea rețelelor Petri de nivel înalt cu temporizare pentru controlul lifturilor
Obiectivele lucrării se axează pe:
Analiza sistemelor de control existente și a algoritmilor care stau la baza lor
Conceperea unui algoritm de control optim și eficient a lifturilor utilizând logica Fuzzy și rețele Petri de nivel înalt cu temporizare
Proiectarea și implementarea unei aplicații Java în care se simulează un sistem de lifturi controlat cu ajutorul algoritmului conceput
Testarea aplicației și verificarea funcționării algoritmului conform specificațiilor
Studiu bibliografic
Liftul – o scurtă istorie
Încă dinaintea erei noastre, oamenii au construit și au folosit mecanisme care funcționau pe baza principiilor unui lift din ziua de azi. Ele erau acționate fie cu forța umană sau animală, fie de niște roți antrenate de forța apei, și folosite în diferite domenii pentru manipularea pe verticală a diferite obiecte, cum ar fi: minerit – pentru ridicarea tonelor de metale exploatate și mutarea lor dintr-un loc în altul – sau construcții – folosite deoarece nu existau alte metode de ridicare a materialelor sau uneltelor grele la o anumită altitudine. Datorită acestor mecanisme primitive, efortul necesar executării acelor acțiuni s-a diminuat semnificativ, iar beneficiile putem spune că erau inegalabile. Aceste mecanisme erau în principal formate din scripeți și manivele, iar siguranța folosirii unui astfel de mecanism era foarte mică, deoarece acei scripeți și frânghiile cu care erau legate nu aveau o rezistență foarte mare si puteau să cedeze în orice moment ducând deseori la accidente sau dezastre.
Mai târziu, au apărut și tendințe ale unor lifturi – în anul 236 î.Hr. se spune că Arhimede ar fi construit primul lift. Până in secolul al XIX-lea, lifturile nu au putut să se dezvolte prea mult datorită lipsei tehnologiei și prezentau un pericol destul de mare, de aceea erau folosite mai puțin pentru transportul persoanelor și mai mult pentru cel al obiectelor. În Figura 2.1 este prezentată schema unui lift proiectat de inginerul german Konrad Kyeser in secolul al XV-lea.
Figura 2.1 Lift din secolul al XV-lea[*1]
În anul 1743, regele Ludovic al XV-lea al Franței avea un lift personal folosit pentru a urca de la etajul 1 la etajul 2, numit ”Sc noastre, oamenii au construit și au folosit mecanisme care funcționau pe baza principiilor unui lift din ziua de azi. Ele erau acționate fie cu forța umană sau animală, fie de niște roți antrenate de forța apei, și folosite în diferite domenii pentru manipularea pe verticală a diferite obiecte, cum ar fi: minerit – pentru ridicarea tonelor de metale exploatate și mutarea lor dintr-un loc în altul – sau construcții – folosite deoarece nu existau alte metode de ridicare a materialelor sau uneltelor grele la o anumită altitudine. Datorită acestor mecanisme primitive, efortul necesar executării acelor acțiuni s-a diminuat semnificativ, iar beneficiile putem spune că erau inegalabile. Aceste mecanisme erau în principal formate din scripeți și manivele, iar siguranța folosirii unui astfel de mecanism era foarte mică, deoarece acei scripeți și frânghiile cu care erau legate nu aveau o rezistență foarte mare si puteau să cedeze în orice moment ducând deseori la accidente sau dezastre.
Mai târziu, au apărut și tendințe ale unor lifturi – în anul 236 î.Hr. se spune că Arhimede ar fi construit primul lift. Până in secolul al XIX-lea, lifturile nu au putut să se dezvolte prea mult datorită lipsei tehnologiei și prezentau un pericol destul de mare, de aceea erau folosite mai puțin pentru transportul persoanelor și mai mult pentru cel al obiectelor. În Figura 2.1 este prezentată schema unui lift proiectat de inginerul german Konrad Kyeser in secolul al XV-lea.
Figura 2.1 Lift din secolul al XV-lea[*1]
În anul 1743, regele Ludovic al XV-lea al Franței avea un lift personal folosit pentru a urca de la etajul 1 la etajul 2, numit ”Scaunul Zburător”. Acesta era situat in exteriorul clădirii și era acționat pe principiul contragreutății cu ajutorul scripeților și frânghiilor – fapt care arată că un timp foarte îndelungat lifturile nu au evoluat aproape deloc, și erau supuse pericolului.[1]
Dar, odată cu declanșarea Revoluției Industriale și evoluția tehnologiei, au început să se perfecționeze și lifturile la mijlocul secolului XIX, fiind acționate de sisteme hidraulice bazate pe ulei sau apă. Acest sistem prezenta avantajul că forța umană sau animală nu mai era necesară pentru a pune în funcțiune liftul, având și o putere mult mai mare, însă dezavantajul era că pistonul sistemului hidraulic avea o lungime egală cu înălțimea la care trebuia să fie ridicat liftul, și era necesar un puț de lungimea pistonului în care acesta să se retragă când liftul trebuia să coboare. Datorită faptului că acel puț era cu atât mai adânc cu cât clădirile erau mai înalte, acest sistem era folosit în general pentru clădirile cu înălțimi mici.
Deoarece aste lifturi încă nu erau destul de sigure, Elisha Otis, un industrialist american, a inventat în anul 1852 un sistem de siguranță pentru lifturi care nu mai permitea căderea lui în cazul în care se rupea frânghia, fiind format dintr-un cadru de lemn care acționa asupra cabinei de lift cu o anumită forță, funcționând în esență ca o frână. În 1854 la New York, Otis a prezentat acest mecanism, și a făcut o demonstrație ridicând liftul câțiva metri după care a tăiat frânghia care îl susținea, liftul fiind oprit de acel sistem după câțiva centimetri de coborâre. În Figura 2.2 este prezentată demonstrația sistemului de sigurantă de la New York de către Elisha Otis.
Figura 2.2 Demonstrația sistemului de siguranță[*2]
După succesul lui Otis, au început să fie construite lifturi de persoane care implementau acest sistem de siguranță, unul din ele fiind funcțional și astăzi. Primul lift de persoane destinat utilizării publice a fost instalat intr-un hotel din New York.[2]
Câțiva ani mai târziu, a fost inventat motorul electric, lucru care a avut un impact major asupra industriei și tehnologiei. Primul care a folosit motorul electric pentru a pune un lift in mișcare a fost inventatorul german Werner von Siemens în 1880 în Germania, motorul fiind amplasat sub cabina liftului, care printr-un sistem de angrenare și cu ajutorul frezelor dințate de pe stâlpii care îl susțineau, deplasa liftul în sus sau în jos. Utilizarea motorului electric a adus mari avantaje, cum ar fi: putere mult mai mare decât forța umană sau sistemele hidraulice folosite atunci, viteză mai mare, utilizare mai ușoară și spațiu ocupat mult mai mic decât pistoanele hidraulice. În anul 1887 motorul liftului folosea un tambur rotativ pe care se înfășura cablul cu care era legat liftul iar motorul nu mai era amplasat sub cabina liftului, însă cu cât erau mai înalte clădirile, cu atât acest tambur trebuia să aibă capacitate mai mare pentru a putea fi înfășurat cablul mai lung. În anul 1900 a apărut motorul de curent alternativ, care a stârnit și mai mult interesul în puterea electrică. 3 ani mai târziu, acesta a fost folosit pentru un nou tip de lift, care era legat de o contragreutate, și cu ajutorul motorului de curent alternativ și un scripete, a fost posibil ridicarea liftului în clădiri cu zeci de etaje la o viteză decentă.[3]
Datorită apariției acestor motoare și nevoii omului de a ajunge cât mai sus, au început să se construiască lifturi pentru clădiri foarte mari, implementând acest sistem, utilizând o contragreutate care nu necesită un motor foarte puternic pentru a pune în mișcare liftul, fiind folosit și în ziua de azi. Însă, problema cea mai mare care se punea atunci, era controlul liftului, deoarece era nevoie ca oprirea să se facă în dreptul erajului cât mai precis și la același nivel cu pragul etajului fără a brusca oprirea lui, dar acest lucru nu era posibil folosind un dispozitiv care controla alimentarea motorului cu energie electrică cu ajutorul unui potențiometru manual. În Figura 2.3 este prezentat acest dispozitiv, având un mâner care în funcție de poziția lui unghiulară alimenta motorul pentru un sens sau altul de mers.
Figura 2.3 Controler manual al liftului[*3]
Sisteme de control a lifturilor
Pentru o perioadă destul de îndelungată de la apariția lifturilor, ele erau controlate manual, fie prin forța umană, animală sau hidraulică, fie cu ajutorul motorului electric care trebuia alimentat cu energie electrică de un operator din interiorul liftului prin intermediul unui dispozitiv electronic în funcție de destinația liftului. În acest ultim caz, operatorul trebuia să aibă o experiență destul de mare cu controlul acelui dispozitiv pentru a putea opri liftul cât mai precis la nivelul unui etaj. Desigur, prezența unui operator în lift pentru a-l pune în mișcare, prezintă un dezavantaj destul de mare, deoarece un loc din lift este tot timpul rezervat – implicit numărul pasagerilor transportați scade – și acel operator va trebui înștiințat printr-un anumit mijloc de informare (un bec, o alarmă etc) când o persoană de la un etaj diferit a chemat liftul.
De aceea s-a simțit nevoia introducerii unui sistem automat de control al liftului, care să nu mai necesite ajutorul omului pentru controlul si mișcarea liftului, fiind nevoie doar ca cineva să selecteze etajul la care dorește să ajungă sau chemarea unui lift la un anumit etaj.[4]
Controlul bazat pe dulapuri cu relee
La începutul secolului XX, au apărut unele sisteme de control automate care foloseau o logică bazată pe relee electromagnetice care aveau scopul de a controla viteza liftului, poziția sa, cât și deschiderea și închiderea ușilor. Aceste sisteme funcționau destul de bine, însă ocupau un spațiu foarte mare și logica de la baza funcționării lor era foarte complexă. Câteva avantaje ale acestor sisteme:
au înlocuit controlul manual cu cel automat
precizia opririi liftului la un anumit etaj mult mai bună
accelerarea și decelerarea automatizate și îmbunătățite
Câteva dezavantaje ale acestor sisteme:
ocupau un spațiu foarte mare
greu de manevrat și instalat (greutate mare)
dificultatea identificării locului unde a apărut o defecțiune
dacă se dorea schimbarea logicii de control, trebuia să se schimbe întreg sistemul, datorită complexității sistemului și legăturilor dintre componente
releele aveau o viață scurtă din cauza uzurii mecanice
Un element important necesar acestui tip de control este reprezentat de „selectorul de etaj”, care determina pozitia curentă a liftului și etajul la care se afla. Acest mecanism folosea o serie de elemente mecanice care se mișcau în același timp cu liftul, iar cu ajutorul unor circuite analogice care „citeau” poziția acestor elemente mecanice, se putea determina poziția și implicit etajul la care se afla liftul.[4]
În Figura 2.4 este prezentat acest sistem de control bazat pe relee, iar în Figura 2.5 este prezentat selectorul de etaj.
Figura 2.4 Dulapul cu releele de control a liftului[*4][*5]
Figura 2.5 Selectorul de etaj[*6]
Controlul liftului folosind PLC-uri
PLC-ul (eng.: Programmable Logic Controller, ro.: Controler Logic Programabil) este un circuit electronic folosit în special în automatizarea instalațiilor și sistemelor industriale, dar nu numai, putând fi folosit la automatizarea unei mari diversități de echipamente. PLC-ul a fost și este folosit cu succes la controlul sistemelor de lifturi din întreaga lume, datorită posibilităților și avantalejor pe care le aduce. Acest controler conține o serie de intrări și ieșiri la care pot fi conectate diverse aparate sau dispozitive, cum ar fi: senzori (de temperatură, umiditate, mișcare etc.), relee, elemente de execuție etc. Acest tip ce control reprezintă o versiune mai nouă dar relativ asemănătoare, a celui bazat pe relee, deoarece și aici sunt folosite unele relee mai performante pentru acționarea ușilor sau motorului, însă diferența majoră o reprezintă microprocesorul integrat în PLC, care oferă o putere de calcul mult mai mare, iar împreuna cu memoria acestuia, oferă avantajul major de a putea modifica programul de control al liftului, fără a modifica structura si elementele de control fizic. Dacă se dorește schimbarea funcționalității liftului, cu ajutorul unui calculator conectat la PLC, se poate rescrie programul foarte ușor, iar lifturile vor funcționa imediat.[5]
Pentru controlul lifturilor folosind PLC-uri, în general, pe intrările acestor dispozitive se vor lega senzorii de indicare pentru fiecare etaj, senzorii pentru închiderea și deschiderea ușilor, semnalul de alarmă etc., iar pe ieșiri vor fi comandate motoarele pentru inchiderea/deschiderea ușilor, motorul principal pentru mișcarea liftului, sistemul de frânare, becuri și alte indicatoare vizuale sau sonore etc. Dacă acest controler dispune de un număr mai mare de intrări și ieșiri, se pot lua în calcul mai multe stări și variabile în funcție de care să se controleze liftul. Toate aceste semnale de control vor fi prelucrate de unitatea centrală a controlerului care va conține un program scris într-un limbaj specific PLC-urilor – în general folosind logica LADDER – oferind și avantajul de a rula acel program în modul de depanare în timp real.[5][6]
În funcție de echipamentele disponibile și capabilitățile PLC-ului, se poare realiza un control standard sau unul mai eficient având și unele opțiuni extra, care pot face din acel sistem de lifturi unul mult mai sigur și mai performant.
În Figura 2.6 este prezentată o schemă generală a semnalelor de intrare/ieșire a PLC-ului pentru controlul unui lift. În partea stângă avem semnalele de intrare de la senzorii de etaj, butoanele de chemare a liftului și cele de selectare a etajului, in partea dreaptă avem semnalele de comandă pentru indicatoarele vizuale și audio (afișaje ale etajului curent, a direcției de mers etc.), pentru deschiderea/închiderea ușilor. În partea de sus avem module de siguranță și a stării de funcționare, iar în partea de jos avem semnalul de închidere sau deschidere a ușilor și nu în ultimul rând alimentarea PLC-ului.[6]
Figura 2.6 Schema semnalelor de control a PLC-ului
Controlul bazat pe PLC este unul destul de bun și relativ simplu, nefiind nevoie de un spațiu mare pentru instalarea acestui sistem, folosindu-se și în ziua de azi, însă prezintă unele limitări din punct de verede al arhitecturii hardware (și software, mai puțin) în cazul în care se dorește ceva mai mult decât un simplu control al sistemelor de lifturi.
Controlul liftului folosind microcontrolere
Un microcontroler este un cip de dimensiuni mici, care încorporează toate elementele principale alre unui computer obișnuit: memorie ROM + RAM, procesor, ceas cât și pini de intrare/ieșire. Prima companie care a produs microcontrolere este Texas Instruments, în anul 1917, inițial fiind posibil programarea lor o singură dată, după care au fost introduse următoarea generație care ofereau posibilitatea de a fi șterse cu ajutorul luminii ultra-violete și reprogramate. Astăzi, tehnologia permite ca aceste microcontrolere să fie șterse electronic și reprogramate rapid.[7]
Aceste dispozitive au evoluat foarte rapid, fiind utilizate în foarte multe domenii datorită funcționalității și adaptabilității lor foarte bună, a dimensiunii și consumului de energie foarte mic și a costurilor relativ mici față de celelalte sisteme folosite pentru controlul unor dispozitive sau procese.
Pentru controlul lifturilor, aceste dispozitive sunt o opțiune foarte bună, datorită posibilității scrierii programului de control în diferite limbaje standard de programare, iar în cazul în care un limbaj nu este suportat de microcontroler, există posibilități de a transforma acel cod în unul compatibil pentru controler. Acesta a fost motivul pentru care s-au răspîndit așa de mult, putând fi folosite și programate aproape de oricine cunoaște un limbaj de programare compatibil, și cu ajutorul unui manual de utilizare se pot realiza conexiunile cu echipamentele care se doresc a fi controlate. De asemenea, se pot scrie programe de control cu o logică foarte complexă și dinamică, iar avantajul major îl reprezintă posibilitatea de a adăuga diferite module periferice microcontrolerului, care ii oferă unele posibilități în plus, cum ar fi: un modul pentru comunicații cu o locație la distanță prin rețele cu fir sau fără fir, module de senzori, interfețe standard pentru comunicare sau transfer de date (de exemplu USB), module pentru conversia semnalelor analogice și digitale (CAN / CNA).
De obicei, se folosesc mai multe microcontrolere, conectate între ele și care execută funcții separate. Programul principal este scris pe controlerul principal, care primește semnalele de la butoanele de chemare a liftului de la fiecare etaj, și îl procesează în scopul de a returna un răspuns utilizatorului și de a da o comandă celorlalte controlere responsabile cu controlul motorului, ușilor, afișajelor de etaj etc.[8]
La controlul unui sistem de lifturi, se vor folosi diferite dispozitive și echipamente de control care de multe ori nu sunt compatibile între ele, dar cu ajutorul acestor microcontrolere si a modulelor extra, se pot scrie programe cu diferiți algoritmi de comunicare care vor ajuta la conectarea si funcționarea cu succes a acestor dispozitive. Cele mai uzuale standarde de comunicare sunt: DeviceNet, Modbus, Profibus, Ethernet, CAN, Foundation Fieldbus.
Sunt prezente și unele dezavantaje ale utilizării acestor microcontrolere:[8]
fiecare buton, LED, afișor are nevoie de un fir dedicat pentru a comunica cu controlerul, iar pentru un sistem mare și complex, poate rezulta într-un număr foarte mare de fire, ocupând un spațiu destul de mare și costuri mai ridicate
în cazul în care unul sau mai multe fire se defectează, va fi foarte greu să se identifice acel defect și să se repare într-un timp scurt, deoarece sistemul de comunicație nu va fi proiectat pentru a expune firele la vedere și pentru a fi accesate ușor
la programarea lor, trebuie să se țină cont de memoria disponibilă și de utilizarea ei
În implementarea acesor sisteme bazate pe microcontrolere va trebui să se țină cont și de întârzierile datorate codificării/decodificării mesajelor dintre componentele sistemului, lungimea mesajelor, viteza de procesare a semnalelor, apariția și tratarea întreruperilor etc.[9]
Tabel 2.1 Structura standard a mesajelor pe magistrala serială CAN[9]
CAN – Controller Area Network
SOF – Start of frame – Începutul mesajului
IDENTIFIER – Un identificator unic al mesajului, care reprezintă, de asemenea, și prioritatea lui
RTR – Remote transmission request – Cererea de transmisie a mesajului
CONTROL – Reprezintă un caracter de control
DATA – Conținutul mesajului transmis
CRC – Cyclic redundancy check – reprezintă un cod de verificare a corectitudinii mesajului trimis
ACK – Acknowledge – Indică dacă mesajul a fost recepționat
EOF – End of frame – Sfârșitul mesajului
În Figura 2.7 este prezentată schema de bază a unui microcontroler, conținând procesorul, memoria, ceasul sistemului, modulele de intrare/ieșire, module de comunicare serială, module analogice, controlor de întreruperi.[9]
Figura 2.7 Schema de bază a unui microcontroler
Metode folosite pentru controlul sistemelor de lifturi
În acest subcapitol se vor prezenta câteva metode folosite pentru controlul lifturilor, identificând elementele cheie alea acestor metode.
Datorită diversității domeniilor în care sunt folosite lifturile și diferența dintre cerințele și specificațiile fiecăruia în parte, face ca pentru fiecare mediu de funcționare să fie necesar un algoritm de control specific lui și care să îndeplinească cerințele cerute.
Controlul unui sistem de lifturi folosind logica Fuzzy
Când vine vorba de controlul unui lift, trebuie să luam în calcul o multitudine de factori variabili, să stabilim relațiile dintre ei, cum influențează controlul, cât de importanți sunt etc., și să stabilim un set de reguli după care va funcționa liftul. De exemplu nu se poate ști cu exactitate numărul de pasageri de la fiecare etaj care așteaptă un lift, numărul de pasageri în acel moment în lift, etajul la care un pasager va vrea să meargă, unde și când va fi chemat liftul, când va apărea o situație de urgență ș.a.m.d, așadar, s-a încercat crearea unui algoritm care să determine automat, în funcție de acești parametrii, care va fi modul de operare al liftului.[10]
Această metodă se împarte în mai multe părți, dintre care, cele mai importante sunt:
O bază de date care conține un set de reguli de funcționare
Reguli de producție
Reguli fuzzy
Modul de decizie a regulilor
Modul de selecție a regulilor
Exemplu al unui mod de control:
IF (condiție cunoscuta) .THEN (implementare secvență): Regulă de producție.
IF (condiție fuzzy) .THEN (implementare secvență): Regulă fuzzy.[10]
De asemenea, acest algoritm implementează un set de reguli de funcționare:
up peak mode – modul în care majoritatea cererilor pentru lift sunt pentru direcția ”în sus” (de exemplu, într-o clădire de birouri, la orele de începere a serviciului, lifturile vor fi chemate în special pentru a urca de la parter la un anumit etaj)
down peak mode – modul în care majoritatea cererilor pentru lift sunt pentru direcția ”în jos” (de exemplu, orele de plecare de la servici)
normal traffic – modul în care lifturile funcționează normal, nefiind suprasolicitată o anumită direcție de mers
idle mode – modul inactiv în care liftul stă
Se ține cont și de timpul de așteptare al pasagerilor pentru un lift, numărul lor și energia consumată de lift pentru servirea pasagerilor, care vor avea un rol important în luarea deciziilor pentru distribuirea lifturilor la fiecare cerere.[11]
În Figura 2.8 este prezentată schema de bază de control a logicii Fuzzy, prin care se primește la intrare o valoare fixă, se asignează acea valoare unui nivel fuzzy, se aplică regulile fuzzy pentru acel nivel de fuzzy, după care se generează un nivel fuzzy nou din care rezultă o altă valoare finală fixă, care reprezintă ieșirea și va fi aplicată mai departe in controlul liftului.
Controlul unui sistem de lifturi axat pe optimizarea coordonatorului și reducerii consumului de energie
Deoarece în ziua de azi există mari probleme legate de mediul înconjurător, nivelul de poluare existent și eventualele mijloace de poluare care ar putea să apară, se încearcă și în cazul lifturilor diminuarea consumului de energie și a impactului indirect asupra mediului.
În cazul sistemelor de lifturi care conțin cel puțin 3 ascensoare, se pune problema găsirii și implementării unui algoritm optim de luare a deciziilor pentru asignarea lifturilor la comenzile pasagerilor de la diferite etaje.
Acest algoritm încearcă reducerea consumului de energie cât mai mult posibil, în același timp păstrând și funcționarea optimă a lifturilor. În cazul în care un lift este așteptare a comenzilor, el va fi asignat deservirii unei comenzi în cazul apariției uneia, deoarece energia necesară opririi și pornirii unui lift este mai mare decât cea necesară mișcării lui, așa că nu va încerca să oprească un lift care deja este în mișcare spre o altă desinație.
Se va lua în calcul timpul mediu de așteptare pentru un lift, timpul necesar parcurgerii distanțelor dintre etaje, timpul necesar persoanelor pentru a intra sau a ieși din lift în funcție de numărul lor. În funcție de acești parametrii impreună cu alți indicatori, algoritmul va asigna fiecărui lift un set de indicatori de cost calculați după un set de reguli de deservire a comenzilor generat în funcție de starea curentă a sistemului, după care se va determina care lift ar fi cel mai potrivit pentru o anumită comandă din punct de vedere al consumului de energie și al timpului de așteptare.[12]
Controlul unui sistem de lifturi bazat pe rețele neuronale
Acest tip de control s-a dezvoltat în urma necesității unui control cât mai bun și mai inteligent, fără a fi nevoie de ajutorul omului prea mult. A apărut in 1994 ca și o îmbunătățire a metodelor deja existente la acel moment de timp, cum ar fi logica Fuzzy, față de care are următoarele avantaje:
poate interpreta fluxul de trafix al lifturilor
au capacitatea de a ”învăța” pe parcursul funcționării
pot să își creeze propriul lor model adaptiv și a anticipa comenzile viitoare
Sunt folosite microcontrolere sau microcomputere pentru punerea în funcțiune a acestor sisteme neuronale, datorită puterii ridicate de calcul și executării instrucțiunilor în paralel grație procesoarelor de ultimă generație.
Și aceste sisteme pot folosi modurile de funcționare peak-up și peak-down, pe lângă care mai apar și modurile în care se ține cont de rata de opriri pe direcția sus sau jos, toate aceste date fiind înregistrate (sau actualizate cele existente) și folosite în permanență de către algoritmul de control pentru a-și putea contrui modelul adaptiv.[13]
În Figura 2.9 sunt prezentate modulele principale folosite într-o rețea neuronală, care inițial trebuie să aibă unele date minime pentru a funcționa și după care să ”învețe” să funcționeze și să se adapteze, iar mai apoi, de-a lungul funcționării v-a memora singur datele statistice și va concepe sau actualiza modelul adaptiv de control.
Acest tip de control, cât și multe altele, funcționează într-o măsură mai mare sau mai mică cu logica Fuzzy, care oferă avantajul de a reduce cazurile de incertitudine sau cele care sunt doar parțial adevărate. De asemenea, este foarte folositoare pentru a combina diferite informații colectate sau estimate și de care trebuie să se țină cont pentru controlul unui lift. Este folosită în combinație cu alte sisteme de control ca și un modul de furnizare a unui rezultat cât mai aproape de cel dorit sau real, și nu are o complexitate foarte mare, putând fi modificată și adaptată cu ușurință la noile cerințe.
Lifturile astăzi
În ziua de azi, când tehnologia evoluează într-o manieră foarte rapidă, este destul de greu să se țină pasul cu ea, mai ales din punctul de vedere al structurii hardware, unde înlocuirea părților fizice poate fi foarte grea, dacă nu, chiar imposibilă, întrucât, de cele mai multe ori se preferă construirea unui lift utilizând componente cât mai noi și mai performante și care să fie utilizabile și compatibile și peste 5-10 ani. De aceea, componentele fizice (cabina liftului, motoarele electrice, senzorii, modulele de control: PLC, microcontroler, microcomputer etc.) trebuie alese, proiectate și interconectate foarte bine, încât dacă se dorește schimbarea modului de funcționare sau control, tot ce va trebui să se facă, va fi doar rescrierea programului din modulul de control. În unele cazuri se va dori adăugarea unor funcționalități, care sunt suportate pe moment de acel sistem, și implementarea lor va fi posibilă doar dacă modulele de control permit adăugarea și conectarea unor module auxiliare separate, care vor fi capabile să îndeplinească acele cerințe noi apărute, și care nu vor afecta funcționalitatea întregului sistem.
Datorită succesului lui Elisha Otis în inventarea sistemului de siguranță care nu permitea liftului să cadă în cazul ruperii frânghiei ce ridica liftul, și care a marcat începutul dezvoltării și utilizării lifturilor tot mai mult, astăzi ne putem bucura de clădirile foarte înalte din întreaga lume, și în viitorul apropiat de cât mai multe clădiri ”inteligente” care se pot controla singure sau sunt controlate de la distanță, ceea ce se poate spune și despre lifturile din aceste clădiri.
Sistemul de monitorizare la distanță al lifturilor (eng.: REM – Remote Elevator Monitoring), dezvoltat de Otis Elevator Company, este un sistem inteligent care identifică posibilele apariții ale problemelor chiar înainte ca acestea să apară, prin identificarea componentelor defecte sau anomaliilor intermitente care nu au fost detectate și au cauzat întreruperea funcționării lifturilor. Dacă sistemul REM detectează o defecțiune, va anunța un centru responsabil de repararea ei, după care o echipă de reparații va fi trimisă la acea locație pentru repararea defecțiunii și punerii în funcțiune a sistemului.
Acest sistem se află într-o continuă dezvoltare încă de la apariția lui la mijlocul anilor 1980, iar în ultimele versiuni, acesa este capabil să trimită datele direct prin Internet.[14]
1 2 3 4 5
Figura 2.10 Modul de operare al sistemului REM
În Figura 2.10 este prezentată o schemă de principiu după care funcționează sistemul REM:[14]
În pasul 1 software-ul de diagnoză monitorizează permanent sistemul și trimite date statistice către sistemul REM
În pasul 2 sistemul REM trimite aceste date către centrul OTISLINE
În pasul 3 datele sunt sortate în funcție de priorități și analizarte de către specialiști
În pasul 4 un specialist OTISLINE anunță tehnicienii dacă este nevoie o intervenție
În ultimul pas, o echipă de tehnicieni sunt trimiși la acel loc pentru a repara sistemul
În ultima vreme, lifturile pot fi contectate la internet, oferind posibilitatea de a fi monitorizate permanent și de a fi controlate cu ușurință de la distanță, oferind accesul la starea sistemului în timp real. De asemenea s-a centralizat și comunicația dintre serviciile de intervenții, care este disponibil 24/24, oferind un soport foarte rapid clienților în cazul apariției unei probleme.
Compania Otis implementează tehnologii de comunicare de ultimă oră iar sistemele actuale sunt modernizate utilizând cea mai nouă tehnologie, aducând lifturile cu un pas mai aproape de viitor și de necesitățile oamenilor.[14]
Analiză și proiectare
Analiză și concepte generale
Când vine vorba de controlul unui proces sau sistem, nu există un algoritm care să fie cel mai bun sau cel mai perfect dintre toate, ci doar unul care este cel mai bun într-un anumit caz sau fiind date anumite specificații sau cerințe de funcționare. Există o multitudine de algoritmi de control, fiecare având avantajele și dezavantajele lui, un comportament specific, cât și un indice de performanță.
Ce este un algoritm?
Un răspuns general ar fi, ”un set de pași necesari pentru a realiza o sarcină”. Folosim acești algoritmi în viața noastră de zi cu zi fără ca măcar să ne dăm seama de acest lucru, de exemplu: când trebuie să învățăm pentru un examen, pentru început ne căutăm materialele necesare, le sortăm și le structurăm, facem un rezumat din care să iasă ideile principale, le împărțim pe zile, în fiecare zi învățăm ceea ce ne-am propus iar la sfârșitul zilei repetăm.
Un algoritm care rulează pe un calculator, folosește același principiu, fie că e folosit pentru găsirea celei mai scurte rute dintre două locații, fie că e folosit pentru securizarea datelor, fie că e folosit pentru controlul unui sistem. Însă, algoritmii după care noi ”funcționăm” nu trebuie să fie întotdeauna foarte clari deoarece putem tolera anumite incertitudini și erori, însă computerul nu le va tolera niciodată. De aceea, când se proiectează un algoritm, trebuie specificate toate detaliile și cazurile de funcționare, ca atunci când aplicăm o ”intrare” acelui algoritm, să obținem același rezultat de fiecare dată, și cu o utilizare a resurselor computaționale cât mai mică.[15]
În general, un algoritm este un program scris într-un limbaj de programare uzual (Java, C, C++ etc.) care rulează pe un computer. Însă problema care apare de cele mai multe ori la scrierea unui algoritm folosind un limbaj de programare normal se referă la faptul că se poate intra atât de mult în detalii, încât ideea principală a algoritmului nu se mai poate observa. Se poate folosi de asemenea și pseudocodul, care reprezintă o combinație a limbajelor de programare împreună cu limbajul uman, care poate fi citit și înteles mult mai ușor.[15]
Așadar un algoritm este orice procedură computațională bine definită, care primește ca și intrare o valoare sau un set de valori, și returnează ca și ieșire un rezultat. Este nevoie de un set de pași computaționali pentru ca aceste intrări să fie transformate într-un rezultat final. Putem spune că un algoritm este corect dacă, pentru orice intrare el va returna un răspuns corect, sau pentru aceiași intrare va returna întotdeauna același rezultat. Un algoritm incorect este acela care nu va răspunde întotdeauna la toate intrările sau va răspunde cu un rezultat greșit.[16]
Diferiți algorimti supuși să rezolve aceiași problemă, nu vor avea același set de performanțe, deoarece, acestea din urmă, depind foare mult de metoda folosită și de timpul necesar executării acesteia. Implicit, performanța algoritmilor depind foarte mult și de puterea de calcul al componentelor hardware.[16]
Deci, chiar dacă în ziua de azi puterea de calcul a computerelor crește semnificativ, alegerea algoritmului optim este întotdeauna o problemă cât și alegerea părților hardware și mai ales a sistemelor de operare care fac legătura dintre algoritmi și părțile fizice.[15]
Ce reprezintă un sistem de control?
Un sistem reprezintă un set de componente interdependente care interacționează între ele și care formează un întreg, un sistem integrat. În industrie, aceste sisteme furnizează produse și servicii virale economiei noastre, cum ar fi: produse alimentare, farmaceutice, electricitate, apă și instalații de purificare a apei, instalații de extragere și prelucrare a petrolului și substanțelor chimice etc. Aceste sisteme sunt supuse unor pericole și dezastre permanente, de aceea ele trebuie supravegheate și controlate permanent, pentru menținerea funcționalității lor normale și în cadrul parametrilor normali.
În general, un sistem de control este implementat pe un computer, și este folosit în multe domenii, mai ales în industrie, unde este necesară monitorizarea și controlul proceselor sensibile și a unor funcționalități fizice. Aceste sisteme permit operatorilor să monitorizeze și să ia decizii de control asupra proceselor controlate. De exemplu, un sistem de control industrial preia datele de la senzorii aflați în proces, pe care le trimite la un sistem central ce le va analiza și va reacționa asupra procesului corespunzător rezultatului primit de la analiza datelor inițiale.[17]
Există două modele principale ale sistemelor de control:
Sisteme de control distribuit
sunt folosite în general în cadrul unui singur proces sau a unei zone geografice mică
Sisteme de Monitorizare, Control si Achiziții de Date
sunt folosite în medii foarte mari care sunt distribuite în zone depărtate
Sistemele de control sunt formate de obicei dintr-o componentă principală sau un centru de monitorizare. Aceste componente principale sunt conectate la stațiile locale de control fie printr-o rețea cablată, fie prin rețele fără fir, fie prin internet; fiecare dintre aceste stații având în principal un PLC sau un microcontroler la care sunt legați senzorii ce furnizează datele obținute prin controlul acelui proces sau sistem.[18]
Așadar un sistem de control este ineficient fără algoritmul său de control, care stă la baza oricărui control automat. Este nevoie atât de un algoritm de control optim și eficient cât și de sisteme de control performante care să poată îndeplini funcțiile și cerințele acelor algoritmi de control.
Limbajul de programare JAVA
Limbajul Java a fost conceput de către James Gosling, Patrick Naughton, Chris Warth, Ed Frank și Mike Sheridan în cadrul companiei Sun Microsystems în 1991, și au fost necesare 18 luni pentru apariția primeri versiuni funcționale. La început s-a numit ”Oak”, dar mai târziu, în 1995, a fost redenumit în ”Java”. Scopul inițial al acestui limbaj nu era destinat internetului ci s-a născut din nevoia unui limbaj de programare care să nu fie dependent de platforma pe care opera, și să poată fi folosit pentru crearea programelor software embedded utilizate de diferite dispozitive electronice, în special controlere.
Cu toate că Java derivă din limbajele de programare C și C++, avantajul acestui limbaj față de celelalte două, este că poate fi rulat indiferent de tipul procesorului sau arhitectura sistemului, pe când, C sau C++ sunt compilate pentru un anumit tip de procesor și nu vor putea fi rulate și pe alte tipuri fără a fi recompilate ca să poată fi rulate și pe acelea.
Odată cu răspândirea Internetului și evoluției paginilor web, a fost necesar un limbaj de programare care să fie suportat de toate platformele și sistemele de operare din lume și care să poată fi folosit pentru crearea paginilor web. Acest moment a marcat propulsia limbajului Java în fruntea limbajelor de programare pentru design.
Cheia soluționării problemelor de securitate și portabilitate este reprezentată de modul în care compilerul Java compilează codul sursă, dar nu rezultă un executabil, ci un fișier (.class, numit și bytecode) ce conține un set de instrucțiuni foarte optimizate ce va fi executat de mașina virtuală Java. Acest fișier este succesul portabilității, deoarece, pentru a rula acel program Java pe orice platformă, tot ce trebuie făcut înainte de executarea lui este să se instaleze mașina virtuală Java compatibilă cu acea platformă, care va putea citi și rula acel fișier ce conține instrucțiunile programului.[19]
Tabel 3.1 Evoluția versiunilor limbajului de programare JAVA
În Tabelul 3.1 sunt prezentate versiunile limbajului de programare Java în ordinea apariției lor, și ce a fost specific fiecărei apariții.
Programarea orientată pe obiect în Java
POO-ul (programarea orientată pe obiect) stă la baza limbajului Java, încât orice program este într-o oarecare măsură orientat pe obiect, și înainte de scrierea unui program este recomandat să se cunoască principiile POO-ului.[19]
Principalii actori în Java sunt obiectele, care memorează date și cu ajutorul unor metode oferă accesul la aceste date și posibilitatea de a le modifica. Un obiect este o instanță a unei clase care definește tipul acestuia și care conține diferite operații de manipulare a datelor. Mai simplu, o clasă este un tipar în care se definesc anumite tipuri de date și metode care acționează asupra datelor, iar un obiect este o instanță a acelei clase, cu care se pot face diferite operații și care sunt create folosind operatorul ”new”. Datele obiectelor sunt socate în variabile, iar operațiile care acționează asupra datelor sunt numite metode, ce pot fi de tip constructor, procedură sau funcție. Fiecare clasă trebuie să definească o metodă constructor, care va fi folosită la instanțierea unui obiect nou, și fiecare program Java trebuie să aibă definită metoda ”main” in clasa principală.[20]
Toate programele software trebuie să conțină date și cod, iar unele sunt scrise în jurul principiului ”ce se întâmplă”, altele în jurul principiului ”ce este afectat”. Aceste două principii guvernează modul în care un program este construit, fiind numite ca și modele orientate pe proces ce caracterizează programul ca și o serie de instrucțiuni liniare, putând face programul să devină foarte mare și foarte complex. De aceea a fost introdus conceptul de programare orientată pe obiect, care rezolvă această problemă, și structurează codul mai mult în jurul datelor. La baza POO-ului stau următoarele principii:[19]
Abstractizarea
acest concept permite ”ruperea” unui sistem complex în subsiteme mai mici, ierarhizate, și care pot fi înțelese și implementate mult mai ușor
Încapsularea
reprezintă un mecanism care ”leagă” împreună datele și codul pentru a le proteja de accesun neautorizat din exterior
aceste accese sunt definite de specificatoarele de acces
în general datele private sunt accesate folosind metode publice create în acest scop
Moștenirea
procesul prin care o clasă moștenește proprietățile unei alte clase părinte și ale părinților părintelui ș.a.m.d., putându-se adăuga și altele pe lângă cele deja moștenite
este un concept cheie care oferă POO-ului posibilitatea de a crește complexitatea într-o manieră ordonată
Polimorfismul
este caracterizat de interfețe, care oferă posibilitatea de a avea ca și punct de start o anumită clasă, care trebuie implementată cu toate metodele acesteia, și pe langă acestea mai pot fi adăugate și altele
oferă posibilitatea scrierii unui cod cât mai generic, datorită implementării unei clase de bază
Aceste 3 concepte, încapsularea, moștenirea, polimorfismul, produc un mediu de programare în care se pot dezvolta aplicații software mult robuste și scalabile decât modeul orientat pe proces. Ele permit ierarhizarea claselor cu ușurință și oferă posibilitatea refolosirii codului, migrarea codului în siguranță, și scrierea unui cod mult mai curat și mai lizibil.
Orice program software trebuie să fie:[20]
Robust
în general, un program trebuie să producă un rezultat bun pentru toate datele de intrare anticipate, însă ca el să fie și robust, asta înseamnă că va trebui să poată procesa și datele pe care nu le așteaptă să le primească, sau cel puțin să reacționeze corect în astfel de situații
Adaptabil
reprezintă capabilitatea unui program de a se adapta la schimbări sau evenimente neașteptate și neplanificate, la eventualele funcționalități noi, cât și să aibă un grad ridicat de portabilitate
Reutilizabil
este reprezentat de faptul ca un cod să poată fi utilizabil și în alt loc sau o altă aplicație existentă sau viitoare; acest concept trebuie implementat cu foarte mare grijă, deoarece erorile pot să apară foarte ușor, dar odată implementat va scuti programatorul de timp și costuri suplimentare
Design pattern în Java
Un design pattern (model de design) este un set de practici sau cheia unei soluții care a fost aplicat cu succest în multiple medii de programare pentru rezolvarea problemelor, rezultând un set de specific de situații. Arhitectul Christopher Alexander descrie acest model ca și o souluție recurentă a unei probleme generale într-un context dat. Termenul de context referindu-se la condițiile sau situațiile în care un anumit model se poate aplica.
Aceste modele pot reprezenta un limbaj comun, folosit pentru împărtășirea experienței acumulate în rezolvarea acestor probleme recurente și găsirea soluțiilor. Modelele nu reprezintă o invenție, ci este o exprimare documentată a celei mai bune metode de soluționare a unei probleme care a fost observată sau descoperită în timpul studierii și construirii diferitelor sisteme software.
Principalul lor scop este de a ajuta la îmbunătățirea calității programului în termeni de reutilizare, întreținere, extindere etc., și de a reduce timpul necesar dezvoltării lui. Câteva aspecte ale acestor modele sunt: au o natură logică, descrierea lor este independentă de detaliile de implementare, sunt foarte generice și pot fi folosite aproape în orice aplicație, nu există sub forma unei componente software ci trebuie implementată explicit de fiecare dată când este folosit etc.[21]
Rețele Petri
Rețelele Petri au fost concepute de către Carl Adam Petri în anul 1962, ca și un instrument folosit pentru modelarea și analiza proceselor. Un avantaj al acestor rețele este că pot descrie procesele prin elemente grafice, ce pot fi înțelese mult mai bine și reflectă funcționalitatea acestora mult mai detaliat. În ciuda faptului că aceste rețele au o formă grafică, ele au o bază matematică foarte temeincă, putând să denote afirmații consolidate despre modul în care un proces este modelat. De-a lungul anilor, aceste rețele au evoluat și au apărut două categorii de rețele: Rețele Petri Clasice și Rețele Petri de Nivel Înalt.[22]
O rețea Petri clasică este formată din locații, tranziții, arce și jetoane:
locația reprezintă un loc unde se adună jetoanele până la un anumi număr maxim predefinit, având ca și intrări și ieșiri una sau mai multe tranziții.
tranziția reprezintă trecerea a unuia sau mai multor jetoane dintr-una sau mai multe locații într-una sau mai multe locații diferite
o tranziție are ca și intrare și ieșire una sau mai multe locații
executarea acestor tranziții este posibilă doar dacă toate locațiile de intrare au cel puțin un jeton și doar dacă exista un arc direct ce conectează în această ordine și în acest sens: locație → tranziție → locație
executarea tranzițiilor este una automată în momentul în care cerințele de mai sus sunt îndeplinite, și presupune extragerea unui jeton din toate locațiile de intrare și inserarea unuia în toate locațiile de ieșire
arcul reprezintă legătura dintre o locație și o tranziție, care specifică sensul în care acel jeton va trece dintr-o locație în alta, și care poate avea o anumită pondere, adică numărul de jetoane necesare pentru a putea trece de la o locație la o tranziție, sau numărul de jetoane ce vor fi inserate într-o anumită locație după executarea tranziției
Staraea unei rețele Petri este dată de distribuția jetoanelor în întreaga rețea, și poate fi reprezentată folosind vectori și matrici.
Figura 3.1 Reprezentarea unei Rețele Petri clasică
În Figura 3.1 este prezentată o schemă a unei rețele Petri clasică, având 3 locații și 3 tranziții, dintre care două având aceiași locație de intrare cât și aceiași locație de ieșire, fiind executate (teoretic) în același moment.
Rețelele Petri de nivel înalt au fost introduse din cauza limitărilor și creșterea lor foarte rapidă în cazul unor modele foarte complexe, făcând acele rețele inaccesibile și greu de înțeles. De aceea, rețelele clasice au fost extinse permițând modelarea unor situații complexe într-un mod accesibil și structurat. Cele mai importante extensii care au fost introduse se refera la:
extensia de ”culoare”
această extensie a fost necesară, deoarece într-o locație sau tranziție nu se putea face deosebirea între diferite jetoane, deoarece în rețelele clasice se afirmă prin definiție că două jetoane aflate în aceiași locație nu pot fi distinse
această extensie va adăuga unui jeton o anumită valoare sau culoare, ce îl va deosebi de celelalte jetoane, și va putea fi categorizat în timpul executării unei tranziții
extensia de temporizare
această extensie a fost necesară pentru calculul performanței unui model realizat cu o rețea Petri
fiecărui jeton îi este asociat o valoare de timp, care reprezintă, momentul de timp la care acel jeton poate fi consumat
în acest caz, o tranziție poate fi executată, doar dacă toate jetoanele ce urmează a fi consumate de aceasta, au valoarea de timp cel puțin egală cu momentul de timp curent
fiecărei tranziții i se poate asocia un moment de timp la care va putea fi executată și o valoare de întârziere ce reprezintă timpul necesar ca acea tranziție să fie executată
valoarea timpului de execuție pentru jetoanele produse de o tranziție, este determinată de timpul la care au fost produse, la care se adaugă încă o valoare determinată de valorile jetoanelor consumate
extensia de ierarhizare
permite introducerea unei structuri ierarhizate în modelele realizate de rețelele Petri
aceste structuri ierarhizate sunt determinate de introducerea unor blocuri în locul tranzițiilor, ce reprezintă sub-rețele formate din tranziții, locații, arce și subprocese
datorită acestor structuri, este posibilă introducerea sub-rețelelor în sub-rețele deja existente, creând o ierarhizare a proceselor foarte complexe, folosind metode top-down sau bottom-up
metoda top-down ”rupe” procesul principal în sub-procese mai mici, până la nivelul cel mai de jos
metoda bottom-up se aplică invers, începând de la cel mai de jos nivel, descriind în detaliu fiecare componentă, după care sunt combinate în procese mai mari
La executarea unei tranziții în cadrul rețelelor Petri de nivel înalt, numărul jetoanelor produse va fi determinat de valoarea jetoanelor consumate, spre deosebire de rețelele Petri clasice în care se știe care va fi numărul jetoanelor produse după executarea unei tranziții.
În Figura 3.2 este prezentată o rețea Petri de nivel înalt cu extensie ierarhică, având o tranziție formată dintr-o sub-rețea, de asemenea, alte două tranziții în care se ține cont de valoarea jetoanelor ce sunt extrase și se decide locul în care acestea vor fi produse.
În Figura 3.3 este prezentată o rețea Petri de nivel înalt cu extensie temporizată, unde fiecare jeton are o valoare de timp de întârziere, și fiecare tranzitie va fi executată la o anumită perioadă de timp.
Noțiuni generale ale diagramelor UML
Diagramele UML au apărut din necesitatea modelării sistemelor software într-un limbaj comun ce poate fi înțeles de oricine, indiferent de domeniul de specializare în care se operează. A fost introdusă de Rational Software în anul 1995, după care în anul 1977 a fost preluată de consorțiul software Object Management Group (OMG), și care este în continuă dezvoltare de numeroase companii mari, cum ar fi IBM, Motorola, Telelogic, NIST etc. Aceste diagrame reprezintă un standard internațional, folosit de toate companiile ce dezvoltă aplicații software.[23]
Aceste diagrame conțin un număr mare de notații al căror scop este de a modela sitemele software în vederea evidențierii tuturor aspectelelor precum: datele, starea, comportamentul, comunicarea, serviciile oferite, cofigurații etc. Aceste aspecte sunt intercorelate și interdependente, fiecare dintre ele formând un limbaj complex, ceea ce face ca utilizarea acestui limbaj să fie foarte provocătoare, furnizând o semantică unificată a sistemelor.[24]
Diagramele UML pot fi de 3 tipuri:[25]
Diagrame structurale
conțin clase, structuri, obiecte, pachete, componente, diagrame de desfășurare
Diagrame funcționale
evidențiază funcționalitatea, dar nu și structura sau comportamentul
includ cazuri de utilizare, diagrame de secvență, diagrame de comunicare, diagrame de sincronizare
Diagrame de comportament
sunt axate pe specificațiile comportamentale
includ diagrama mașinilor de stare, diagrama de activități
Diagramele de bază, care sunt neaparat necesare pentru construirea unui UML, sunt: diagrama claselor, diagrama de stare, diagrama de secvențe. Pe lângă aceste diagrame, se pot adăuga și altele dacă se dorește evidențierea altor aspecte ale sistemului.
Proiectarea aplicației
Arhitectura aplicației
În această lucrare este prezentată proiectarea și implementarea unei aplicații Java ce va simula funcționarea și controlul unui sistem de lifturi. Această aplicație este formată din două componente principale:
O interfață grafică, unde se află:
o secțiune în care se pot vizualiza lifturile împreună cu starea lor
butoanele de chemare a unui lift la un anumit etaj
butoanele de selectarea a etajului dorit, care în mod normal se află în interiurul liftului
un selector care are scopul de a indica, care lift este controlat de butoanele de selectare a etajului
O logică de control bazată pe rețele Petri de nivel înalt temporizate, care poate fi împărțită în:
un modul de alegere a unui lift pentru deservirea unei comenzi și de adăugarea unei opriri la un anumit etaj provenită de la panoul din interiorul liftului
un modul de control care determină următoarea oprire a liftului și care, de asemenea, este responsabil de controlul și mișcarea acestuia
Figura 3.4 Diagrama de componente ale aplicației
În Figura 3.4 este prezentată arhitectura aplicației și componentele principale din care este alcătuită. Interfața grafică stă la baza funcționării și utilizării aplicației, deoarece este locul de unde vor porni comenzile pentru lifturi, și pe ea se poate observa starea lifturilor și a comenzilor. Componenta de redesenare este responsabilă doar cu apelarea unei funcții ce va redesena toate elementele interfeței grafice. Componenta de alegere a unui lift este resposabilă de alegerea unui lift în funcție de anumiți parametri, pentru a servi o comandă. Componenta de control are sarcina de a calcula următoarea oprire a unui lift luând în considerare cererile provenite de la panoul din interiorul liftului, cât și cererile provenite de la butoanele de pe panoul exterior al fiecărui etaj. Componenta de mișcare a lifturilor este responsabilă de modificarea coordonatelor liftului pe interfața grafică. Componenta de afișare a stării lifturilor colorează butoanele de pe ambele panouri când acestea au fost apăsate, și afișează etajul curent al unui lift, și direcția de mers al acestuia.
Diagrame UML
Diagrama Use Case
Prin definiție, diagrama Use Case descrie o interacțiune coerentă și direcționată de un actor cu un sistem, în care, la început există un domeniu declanșat, iar la sfârșit este definit rezultatul valorii domeniului.
Un actor este un rol care interacționează cu sistemul, și se află înafara sistemului. Acest rol poate fi insușit de un individ sau de un sistem extern.[23]
În general aceste diagrame descriu relațiile dintre actori și sisteme, acțiunile declanșate de către actori și rezultatul fiecărei acțiuni în funcție de actorul care a decalnșat-o.
Figura 3.5 Diagrama Use Case
În Figura 3.5 este prezentată diagrama Use Case a acestei aplicații, în care utilizatorul poate interacționa doar cu butoanele de chemare a liftului la un anumit etaj, din exterior, și cele de selectare a etajului dorit, din interiorul liftului. Acționarea butonului de chemare a unui lift la un anumit etaj, va apela mai departe controlerul ce va asigna un lift acelei comenzi. Acționarea butonului de selectare a unui etaj, va apela controlerul responsabil cu acel lift și va adăuga o cerere de oprire pentru etajul respectiv. Mai departe, utilizatorul va putea vedea starea lifturilor și a comenzilor existente.
Diagrama de clase
Această diagramă este folosită pentru a descrie structura și comportamentul obiectelor din lumea reală transpuse în lumea virtuală. Aceste clase și obiecte reprezintă centrul programării orientate pe obiect, clasa fiind cel mai mic bloc al unui sistem orientat pe obiecte, și este interconectată cu alte strucuri mai mari prin diferite relații de asociere. O clasă descrie structura și comportamentul obiectelor care au aceeași semantică și caracteristică, structura fiind descrisă prin atribute iar comportamentul prin operații și funcții. Obiectele sunt elemente concrete create după structura unei clase.[23] Clasele din această diagramă sunt conectate între ele prin diferite tipuri de relații, cum ar fi: asociere, multuplicitate, asociere directă, agregare, compoziție, polimorfism, moștenire.
Figura 3.6 Diagrama claselor
În clasa Main se declară și se instanțiază clasele Painter, Elevator, Coordinator și OutsidePanel, și se adaugă elementele grafice pe interfața grafică. În clasa Coordinator se declară și se instanțiază clasele Controller, clasa InsidePanel, și de asemenea această clasă este resoponsabilă cu procesarea comenzilor de la butoanele de pe panourile interioare și exterioare, și alegerea liftului pentru deservirea comenzilor. În clasa Controller se declară și se instanțează clasele DoorsController și ElevatorWorker, pe lângă aceste lucruri, mai este responsabilă pentru setarea cererilor de oprire primite de la Coordinator pentru liftul de care răspunde acel Controller și de înștiințarea Coordinator-ului de starea liftului. Clasa Elevator are rolul de a afișa deschiderea și închiderea ușilor, și de a încărca și descărca liftul. Clasa Painter este folosită doar ca mijloc intermediar spre clasa Jpanel care redesenează interfața grafică. PainterWorker extinde clasa Thread, putând rula independent față de celelalte fire de execuție, și are rolul de a apela la un anumit interval de timp funcția de redesenare a interfeței grafice. ElevatorWorker extinde, de asemenea, clasa Thread și are rolul de a calcula opririle liftului și de a modifica poziția lifturilor pe interfața grafică în funcție de direcția lor. Clasa DoorsController este utilizată de fiecare Controller pentru apelarea funcțiilor de deshidere și închidere a ușilor liftului. Clasa Dir de tip enum este folosită pentru direcțiile lifturilor cât și a direcțiilor cererilor de lifturi de la panourile exterioare.
Diagrama de obiecte
Figura 3.7 Diagrama de obiecte
Diagrama de obiecte ilustrează relațiile dintre obiectele aplicației, dependența dintre ele și utilizarea lor.
Diagrama de colaborare dintre obiecte
Figura 3.8 Diagrama de colaborare dintre obiecte
În această diagramă se evidențează sensul comunicării dintre obiecte și modul în care acestea comunică.
Diagrama de activități
Scopul acestei diagrame este de a descrie modul în care o aplicație decurge de la startarea acesteia până la oprirea ei. Ea reprezintă un model ce descrie secvența acțiunilor principale, care poate fi împărțită la rândul ei în mai multe acțiuni ce pot fi concurente, sincronizate sau, în funcție de unele condiții și decizii, să aleagă o ramură de execuție sau alta.
Această diagramă are posibilitatea de a descrie acțiunile elementare și executabile, cum ar fi acțiuni de scriere sau citire a unei valori, crearea unui obiect etc., permițând crearea unui model axat pe fluxul acțiunilor unui sistem.
Acest model conține un număr de activități, definite ca și un flux format din ațiuni principale, care poate fi paralel sau sincronizat, ori ramificat într-un număr de branșe care într-un final pot fi unite într-o singură ramură. Este foarte similară cu diagrama mașinilor de stareă, însă fiecare activitate este un element separat având comportamentul său specific.
În Figura 3.9 se poate observa diagrama de activități a acestei aplicații, care pune accentul pe funcționalitățile și acțiunile principale, cum ar fi: inițializarea interfeței grafice cu butoanele de chemare a liftului și de selectare a etajului, afișarea lifturilor în acea interfață, interacțiunea utilizatorului cu panourile de comandă, procesarea comenzilor apărute, alegerea celui mai potrivit lift pentru deservirea unei comenzi, controlul și mișcarea acestuia, afișarea permanentă a stării liftului și a comenzilor, calcularea opririlor și verificarea existenței unor alte comenzi neprocesate.
Figura 3.9 Diagrama de activități
Diagrama de secvențe
Figura 3.10 Diagrama de secvențe
Această diagramă evidențiază interacțiunile dintre elementele sistemului și cine de cine este apelat. O interacțiune descrie comunicarea dintre obiecte bazată pe mesaje de forma unui semnal sau apelarea unei funcții, care specifică comportamentul sistemului la fel ca și o diagramă de activități sau de stare. Elementele interacțiunii sunt reprezentate de linii de viață ale obiectelor, specificând modul de comunicare dintre acestea.
O interacțiune reprezintă un scenariu specific și elementele participante la aceasta, arătând originea și destinația mesajelor transmise. O linie de viață a unui obiect descrie numele, tipul și durata de viață a obiectului. Un mesaj reprezintă o comunicare între două linii, ce poate fi sincronă sau asincronă, și poate invoca o operațiune, transporta un semnal sau crea un obiect nou. Aceste mesaje stau la baza comunicării dintre elemente și a funcționării lor, deoarece transmit informația necesară executării sarcinilor și luarea deciziilor în funcție de rezultatele anterioare ale obiectelor. Un mesaj sincron așteaptă un răspuns de la elementul căruia i-a transmis un semnal, iar cel asincron este doar transmis fără a mai aștepta o confirmare de primire.[23]
În această diagramă sunt reprezentate obiectele principale alea aplicației și modul în care acestea comunică și schimbă mesaje. În timpul inițializării aplicației aceste obiecte sunt create, iar cele care extind clasa Thread sunt startate și setate în modul wait. Lanțul de evenimente este declanșat cu acționarea de către utilizator a butoanelor de pe panourile exterioare sau interioare, ce vor trimite cereri către coordonatorul central care va valida aceste mesaje, în cazul în care mai există o astfel de cerere, ea va fi ignorată. Dacă cererea a fost acceptată, coordonatorul va alege liftul potrivit pentru aceasta, și o va treimite acestuia, ce va porni firul de execuție responsabil cu mișcarea liftului, care la rândul său va controla închiderea și deschiderea ușilor, afișarea stării pe interfața grafică și verificarea unor alte cereri existente, după care va intra înapoi în modul wait.
Implementarea aplicației
În această lucrare se realizează simularea unui control de lifturi folosind THLPN, având o interfață grafică pe care, utilizatorul poate acționa butoanele de chemare a liftului la un anumit etaj pentru o anumită direcție și de asemenea acționarea butoanelor de selectare a etajului dorit. Interfața grafică are atașată un fir de execuție ce o va redesena la un anumit interval de timp presetat, pentru a se putea observa mișcarea lifturilor. Fiecare lift în parte având, de asemenea, câte 2 fire de execuție dedicate: unul responsabil cu modificarea coordonatelor liftului pe interfața grafică și unul cu rolul de a opera ușile liftului.
În controlarea lifturilor s-a folosit o logică bazată pe evenimente, utilizând modelul rețelelor Petri de nivel înalt, iar pentru determinarea liftului ce va deservi o comandă de cerere, a fost utilizată logica fuzzy luând în calcul un anumit număr de parametrii.
Pentru implementare s-a folosit limbajul de programare Java SE și mediul de dezvoltare Eclipse Juno, utilizând doar clasele regăsite în pachetul Java și unele clase noi, create special pentru funcționarea acestei aplicații.
Crearea și configurarea aplicației în mediul de dezvoltare Eclipse
Eclipse este un mediu de dezvoltare open-source și gratuit, destinat dezvoltării, în principal, a aplicațiilor Java. Are o interfață grafică prietenoasă, ușor de utilizat, oferind funcționalitățile de bază necesare dezvoltării aplicațiilor. Conține modulul Intellisense, ce permite afișarea opțiunilor posibile, chiar după prima literă de cod scrisă, ajutând foarte mult la scrierea unui program și economisirea timpului. De asemenea, oferă posibilități de integrare cu diferite plugin-uri ce pot oferi funcționalități în plus sau suport pentru diferite module extra.
Pentru crearea aplicației am început prin crearea unui proiect Java în mediul Eclipse, în care am creat un număr de clase Java, urmând acești pași:
După ce aplicația s-a pornit, am selectat din meniul ”File” → ”New” → ”Java Project”
În fereastra nouă care se deschide am introdus în câmpul ”Project name” numele proiectului: ”Licenta”, iar restul setărilor au rămas neschimbate și am apăsat butonul ”Finish” (Figura 4.1)
După apăsarea butonului ”Finish” proiectul a fost creat și conține un folder ”src” în interiorul căruia se află pachetul inițial ”default package”, pe lângă care am creat și pachetul ”Enums” necesar implementării aplicației
Pentru crearea fișierelor (claselor) java, am selectat pachetul în care am dorit crearea lor, apăsând click-dreapta pe acel pachet și am ales ”New” → ”Class”
În fereastra ce apare am introdus un nume pentru acea clasă ce trebuie să fie unic în acel pachet, am specificat tipul clasei (în cazul în care clasa ce se crează va conține metoda ”main” trebuie neaparat ca tipul ei să fie ”public”), iar restul setărilor au rămas cele inițiale; am selectat butonul ”Finish” pentru crearea clasei
În pachetul ” default package” am creat toate clasele necesare implementării aplicației în următoarele fișiere având același nume ca și clasa: Main.java, Coordinator.java, Controller.java, Elevator.java, ElevatorWorker.java, DoorsController.java, InsidePanel.java, OutsidePanel.java, Painter.java, PainterWorker.java
În pachetul ”Enums” am creat clasa și, implicit, fișierul Dir.java
Figura 4.1 Crearea unui nou proiect Java
Figura 4.2 Crearea unei noi clase Java
Figura 4.3 Forma finală a proiectului după crearea claselor și pachetelor
În Figura 4.1 sunt ilustrați pașii 1 și 2 pentru crearea unui proiect Java în Eclipse și sunt specificați pașii secundari ce trebuie urmați în vederea creării proiectului.
În Figura 4.2 sunt reprezentați grafic pașii 3, 4 și 5 și este reprezentat modul în care se procedează pentru crearea claselor.
În Figura 4.3 este prezentată forma finală a proiectului după crearea tuturor pachetelor și claselor necesare pentru implementarea aplicației.
În momentul acesta proiectul este creat și configurat, având toate clasele necerare pentru implementarea simulării sistemului de lifturi.
Dezvoltarea aplicației
Această aplicație constă într-o singură interfață grafică, ce poate fi împărțită în două componente principale:
O componentă ce conține panourile de comandă împreună cu botoanele acestora, având în spate o logică de control complexă
O componentă responsabilă cu afișarea și mișcarea lifturilor grafice și a stării acestora.
Aceste două componente pot fi împărțite la rândul lor, în mai multe sub-componente secundare, având fiecare un anumit rol specific în funcționarea aplicației.
Clase folosite și utilizarea lor
Clasa Main
Această clasă este principala clasă a programului și totodată cea mai importantă, deoarece conține metora ”main” care este necesară pornirii aplicației. În această clasă se instanțiază clasele Painter, Elevator, OutsidePanel, pe lângă acestea, sunt create și următoarele elementele și obiecte grafice:
up0, up1, down1, up2, down2, up3, down3, down4 sunt obiecte de tip OutsidePanel, și extind clasa BasicArrowButton
aceste obiecte sunt folosite pentru afișarea butoanelor de chemare a liftului de la fiecare etaj în parte și de trimiterea comenzilor către coordonatorul principal
în Figura 4.4 se prezintă grafic aceste butoane
Figura 4.4 Butoanele de chemare a liftului
lElevLoad1, lElevLoad2 sunt obiecte de tip Label, folosite pentru indicarea pe interfața grafică, a încărcăturii curente a lifturilor exprimată în procente
aceste indicatoare pot fi vizualizate în Figura 4.5, și sunt compuse din două obiecte de tip Label statice, cu mesajul ”Incarcare lift 1” (lift 2), și celelalte două obiecte dinamice, ce iși schimbă mesajul în funcție de încărcătura liftului
Figura 4.5 Indicator de încărcare a lifturilor
lElevList reprezintă un obiect de tip Choice, conținând o listă cu ID-urile lifturilor folosită pentru selectarea liftului ce va fi operat de butoanele de pe panoul de selecție a etajelor de destinație
este conectat cu obiectul de tip Label, ” lSelLabel”, ce afișează mesajul ”LiftSelectat”, ajutând la indicarea liftului selectat
în Figura 4.6 este reprezentat acest obiect de selecție, ce va face posibilă acționarea panoului de control interior
Figura 4.6 Selectarea liftului ce va fi operat de panoul de control
Clasa Painter
Această clasă este una simplă, responsabilă cu desenarea efectivă a lifturilor pe interfața grafică. Pentru ca acest lucru să fie posibil, extinde clasa JPanel, moștenind-ui funcționalitățile, printre care și funcția ”paintComponent” ce are posibilitatea de a desena elemente grafice.
Clasa conține două obiecte de tip Elevator, reprezentând cele două lifturi, și un obiect de tip PainterWorker. Pentru a face posibilă desenarea obiectelor grafice, această clasă citește coordonatele, dimensiunile și culoarea fiecărui lift, și desenează un dreptunghi la acele coordonate având acele dimensini și culori.
În Figura 4.7 este reprezentată componenta de pe interfața grafică ce este (re)desenată în permanență de către această clasă. Elementele (re)desenate sunt liniile verticale si orizontale ce reprezintă pereții de delimitare a etajelor și lifturilor, și cele două dreptunghiuri albastre ce reprezintă lifturile.
Figura 4.7 Reprezentarea componentei desenate de către clasa Painter
Clasa PainterWorker
Implementarea acestei clase este realizată cu ajutorul clasei ”Thread”, pe care o extinde, și care utilizează metoda ”run” pentru a apela, într-o buclă infinită și la intervale de timp predefinite (50 ms), metoda ”repaint” a componentei de desenare, care mai depare apelează metoda ”paintComponent” din clasa Painter. Acest lucru face ca toate elementele din acea componentă să fie redesenate și creează efectul de mișcare a lifturilor. Avantajul moștenirii clasei Thread, face ca acest obiect responsabil cu invocarea redesenării, să ruleze pe un fir de execuție separat și independent de restul aplicației, neblocând interfața grafică și nici funcționalitatea acesteia.
Clasa OutsidePanel
Scopul acestei clase este doar de a moșteni proprietățile și metodele clasei părinte ”BasicArrowButton” și de adăugarea unei noi proprietăți ”lFloor” de tip ”int”, care ajută la identificarea fiecărui buton de chemare a liftului, și mai specific, identificarea etajului la care s-a facut această solicitare, și de adăugarea clasei Coordinator ca și element de ”ascultare” și tratare a evenimentelor generate de acest buton. Este clasa care reprezintă fizic și grafic butoanele de chemare a liftului de la fiecare etaj. Cu toate că este o clasă foarte mică fără o oarecare funcționalitate sau acțiune proprie, joacă un rol foarte important, gen al unei interfețe, prin care se face legătura dintre utilizator și funcționalitatea aplicației.
Aceste butoane au, de asemenea, și rolul de a indica dacă o comandă a fost validată și acceptată sau nu, prin colorarea acestuia în culoarea roșie în cazul în care a fost acceptată acea comandă.
Reprezentarea grafică a acestor butoane poate fi observată atât în Figura 4.4 cât și în Figura 4.7.
Clasa InsidePanel
Reprezintă o altă componentă grafică ce extinde clasa ”JPanel”, doar că de data aceasta, acea clasă nu este folosită pentru (re)desenarea unor elemente, ci doar pentru crearea unui nou spațiu pe interfața grafică ce delimitează panoul de control interior și grupează butoanele de control de restul componentelor din interfață.
Această clasă conține un vector (Array) de tip Button, având dimensiunea egală cu numărul etajelor, și care conține butoanele de selectare a etajului la care utilizatorul dorește să ajungă liftul ce a fost selectat să fie comandat de acest panou. Pe lângă acest vector, mai conține două elemente de tip Button: ”ALARMA” și ”M”. Butonul de alarmă va opri imediat liftul selectat când acest buton va fi acționat. Butonul ”M” are rolul de a selecta modul de funcționare al liftului, acționând acest buton, acel lift va intra în modul manual, adică va răspunde doar comenzilor ce provin de la panoul de control intern; această funcționalitate fiind benefică atunci când există cazuri speciale sau de urgență și se dorește să se ajungă de la un etaj la altul cât mai rapid și fără a opri la etaje intermediare.
Figura 4.8 Componența panoului din interior
În Figura 4.8 este prezentat panoul interior de control al liftului, conținând butoane de selectare a etajului și care semnalizează prin schimbarea culorii acestora în roșu atunci când acea comandă a fost validată și preluată.
Acționarea repetitivă a butoanelor ”ALARMA” și ”M” va duce la activarea, respectiv dezactivarea acestora.
Această clasă joacă de asemenea rolul de ”ascultare” și primire a comenzilor provenite de la butoanele sale, și trimiterea lor mai departe coordonatorului principal.
Clasa Coordinator
Este clasa principală din punctul de vedere al controlului și coordonării aplicației, fiind responsabilă cu primirea tuturor cererilor, fie că vin de la panourile interioare, fie de la cele exterioare, analizarea și validarea lor, și distribuirea lor către controlerele lifturilor ca și rezultat al unei logici de alege a liftului cel mai potrivit pentru o anumită comandă în funcție de un anumit număr de parametrii. Pe scurt, face legătura între acțiunile create de utilizator și controlul efectiv al lifturilor.
Metodele principale ce se regăsesc în această clasă sunt folosite pentru alegerea unui lift în cazul apariției unei cereri de lift, notificarea controlerului responsabil cu liftul selectat în selectorul din interfața grafică și semnalarea acestuia de setarea modului Manual și al celui de Alarmă, selectarea controlerului ce va primi cererile de la panoul de comandă interior, cât și ștergerea cererilor deservite de către un lift și semnalizarea acestui eveniment prin setarea culorii butoanelor de unde au provenit comenzile, în culoarea inițială, atunci când un controler semnalează acest lucru
Această clasă implementează interfața ”ActionListener” pentru a avea posibilitatea de a ”asculta” și procesa comenzile primite de la panoul exterior de comandă.
Clasa conține de asemenea și un număr de obiecte:
lCtrl1, lCtrl2, lSelCtrl: sunt o primele două reprezentând controlerele celor două lifturi, cărora le va trimite comenzile primite de la utilizator, iar ce-l de-al treilea reprezintă controlerul liftului care este selectat pe interfața grafică pentru primirea comenzilor de la panoul de control intern
lOPanels este un obiect de tip ”LinkedList”, ce conține butoanele de pe panourile exterioare care au trimis o cerere și a fost acceptată de către un lift, de asemenea ține evidența acestor butoane pentru a fi folosite la ștergerea cererii de la etajul respectiv pentru acel lift, în momentul în care acesta din urmă ajunge la un etaj la care a fost solicitat
lInsideP reprezintă un obiect de tip ”InsidePanel” și este folosit doar pentru a colora butoanele de pe panoul de comanda intern din interfața grafică corespunzător stării cererilor create de acestea; butoanele vor avea culoarea roșie în cazul în care cerera a fost acceptată, iar în caz contrar vor rămâne neschimbate
lUpRequests și lDownRequests sunt doi vectori (Array) de tip boolean, ce au un rol important la procesarea și deciderea acceptării unor noi comenzi provenite de la panourile exterioare pentru un anumit etaj
Clasa Controller
Această clasă este responsabilă de controlul liftului asignat acesteia și de primirea comenzilor de la coordonatorul central. Aceste comenzi vor fi memorate de către controler și folosite de clasa ElevatorWorker pentru determinarea următoarei opriri a liftului.
De asemenea are responsabilitatea de a deschide ușile liftului când acesta a ajuns la un anumit etaj, sau se alfă la un etaj de la care provine o cerere și liftul se află în starea de repaus. Acest obiect va primi o notificare de la lift în momentul în care acesta se va opri la un anumit etaj, iar controlerul va șterge cererile pentru acel etaj din memoria lui și va trimite o notificare și controlerului principal care va șterge la rândul său cererile din memoria sa, notificând utilizatorul de acest eveniment.
În cazul în care se selectează unul din modurile manual sau alarmă, controlerul este responsabil de setarea acestor moduri și punerea lor în funcțiune. Are funcționalitatea de a semnaliza pe interfața grafică etajul curent al liftului și direcția de deplasare a liftului asignat, trimiterea notificărilor de oprire a unui lift către coordonatorul principal și indicarea faptului că un lift este plin sau dacă un anumit lift aparține de acest controler.
Clasa conține următoarele obiecte:
lElev reprezintă un obiect de tip Elevator, fiind folosit pentru crearea obiectului lElevW de tip ElevatorWorker, încărcarea și descărcarea liftului, memorarea cererilor provenite de la panoul de control intern, cât și pentru indicarea momentului când liftul este plin
lElevW este un obiect de tip ElevatorWorker având rolul de a seta o cerere pentru acel lift și a-l porni și de a opri sau porni liftul în cazul acționării butonului de alarmă
lCoord, un obiect de tip Coordinator, care face legătura între această clasă și coordonatorul principal, și prin intermediul căruia se trimit notificările de ștergere a cererilor din memorie
lDCtrl de tip DoorsController, este responsabil de crearea legăturii între clasa ce operează ușile liftului și liftul controlat și de a deschide ușile liftului în momentul în care este semnalată o oprire a acestuia sau o cerere de la același etaj
lDir este un obiect de tip Dir folosit pentru specificarea direcției de mers a liftului
lStopAtFloor reprezintă un vector de tip Dir ce conține toate cererile de oprire la un anumit etaj acceptate și care specifică și direcția pentru care s-a făcut acea cerere
lInsideReqs fiind un vector de tip boolean, care memorează cererile provenite de la panoul de control intern, folosit de clasa ElevatorWorker pentru calcularea următoarei opriri și de asemenea pentru indicarea cererilor existente pe butoanele panoului intern, cum sunt indicate și în Figura 4.8
Clasa Elevator
Este clasa specifică ce modelează un lift real, folosită doar pentru specificarea detaliilor legate de lift cum ar fi: ID-ul, încărcătura curentă, capacitatea maximă, coordonatele, dimensiunile și culoarea acestuia pe interfața grafică. Este folosită și de alte clase pentru a accesa aceste detalii necesare în logica de control al sistemului.
Conține și unele metode utilizate pentru: afișarea următoarei opriri a liftului respectiv, deschiderea și închiderea ușilor care sunt simulate prin schimbarea culorii liftului (albastru – uși închide, verde – uși deschise), cât și pentru simularea încărcării și descărcării liftului cu persoane.
Obiectele și scopul lor din această clasă sunt următoarele:
x1, y1, width, height, ID, load, maxLoad sunt proprietăți de tipul int folosite pentru specificarea coordonatelor și dimensiunilor liftului, ID-ul acestuia, încărcarea curentă și capacitatea maximă
elColor, un obiect de tip Color, folosit pentru simularea deschiderii și închiderii ușilor, cât și indicarea atunci când liftul este plin; obiectul este folosit de clasa Painter ce va desena lifturile cu această culoare pe interfața grafică
isFull și lDoorsOpen sunt proprietăți de tipul boolean pentru a indica dacă liftul este plin sau dacă ușile sunt deschide; sunt folosite de clasa Controller și de clasa ElevatorWorker
loadAt și unloadAt, doi vectori de tip boolean folosiți pentru memorarea etajelor la care ”persoanele” doresc să ajungă sau etajelor de la vor urca în lift; cu ajutorul lor se va simula încărcarea și descărcarea liftului, folosind un obiect de tip Random pentru generarea aleatoare a încărcării, respectiv descărcării
lElevLoad și lElevStop sunt două obiecte de tip Label folosite pentru indicarea pe interfața grafică a încărcării curente a liftului și următoarea oprire a liftului
Figura 4.9 Reprezentarea stării normale și a ușilor deschise
Figura 4.10 Reprezentarea stării unui lift încărcat la maxim
Figura 4.11 Reprezentarea modurilor ”ALARMA” și ”MANUAL”
În Figura 4.9 se poate observa în partea sângă a imaginii reprezentarea pe interfața grafică a aplicației a unui lift în modul de operare normal și care nu este încărcat la maxim, având culoarea albastră. În partea dreată a imaginii este reprezentat un lift cu ușile deschise având culoarea verde, oprit la un etaj la care fie a fost chemat, fie a avut o cerere pentru sau la acel etaj.
În Figura 4.10 este reprezentat în partea stângă un lift având încărcătura maximă admisă, fiind de asemenea, indicată în partea de sus a imaginii, după cum se poate observa. Acel lift este reprezentat cu o culoare roșie, reprezentând modul în care nu mai răspunde la comenzile provenite de la panourile de comandă externe. Liftul din partea dreatpă a imaginii este reprezentat ca fiind în modul normal de lucru și desenat cu culoarea albastră, având o încărcătură aproape de jumătatea valorii maxim admise.
În Figura 4.11 sunt reprezentate modurile ”ALARMA” și ”MANUAL”. În partea stângă se află liftul cu modul de ALARMA selectat, fiind reprezentat cu o culoare neagră. În acest mod, liftul nu mai răspunde la nicio comandă, indiferent de unde provine aceasta, până ce acest mod nu este dezactivat. În partea dreaptă se regăsește liftul aflat în modul MANUAL, fiind reprezentat de o culoare turcoaz. În acest mod, liftul răspunde doar comenzilor provenite de la panoul de comandă intern. Deasupra fiecărui lift se poate observa selectarea acelui mod de pe panoul de comandă intern.
Clasa DoorsController
După cum ii spune și numele, este o clasă ce funcționează ca și un controler pentru deschiderea și închiderea ușilor, fiind legată de controlerul unui lift și de liftul în sine. De asemenea, această clasă extinde clasa Thread pentru moștenirea metodei ”run” în care se va simula deschiderea/închiderea ușilor. Pentru acest lucru va apela metodele ”OpenDoors” și ”CloseDoors” din clasa Elevator.
Am ales pentru implementarea acestei clase ca și un fir de execuție separat și independent pentru a nu bloca sau întârzia memorarea eventualelor apariții ale unor cereri noi, și pentru a face posibilă menținerea deschisă a ușilor când un buton de la sau pentru acel etaj este apăsat fie din interior, fie din exterior; făcând așadar, aplicația mult mai receptivă la comenzile utilizatorului. Funcționarea acestei componente este realizată într-o buclă infinită, fiind oprită și pornită cu ajutorul metodelor wait() și notify().
În momentul în care un lift s-a oprit la un etaj, sau este necesară deschiderea ușilor, controlerul acelui lift, va notifica această clasă pentru deschiderea ușilor, și de asemenea, această clasă va notifica acel controler în momentul în care ușile s-au închis.
Conține două obiecte principale:
lElev, de tip Elevator, folosit pentru a face legătura cu clasa Elevator și pentru a apela metodele de simulare a deschiderii și închiderii ușilor din acea clasă
lCtrl, de tip Controller, pentru legătura cu controlerul liftului și anunțarea acestuia în momentul în care ușile sunt închise și liftul se poate mișca
Rezultatul acestei clase poate fi observat în Figura 4.9, la liftul din partea dreaptă a imaginii, având o culoare verde.
Clasa ElevatorWorker
Această clasă este cea mai importantă din punct de vedere al controlului liftului și calculării rutelor acestuia. Pentru funcționarea acesteia este necesară extinderea clasei Thread și crearea unui nou fir de execuție independent, oferind posibilitatea funcționării lifturilor în paralel, neblocând interfața grafică și acceptând noile comenzi apărute. Mișcarea efectivă a liftului se realizează într-o buclă infinită aflată în interiorul metodei run(). Această buclă este controlată de metodele wait() și notify(), fiind apelate în momentul în care un lift se oprește sau se pornește. Conține două metode interne, una folosită pentru detectarea următoarei opriri de la panoul de control intern pe direcția de mers a liftului, iar cealaltă fiind folosită pentru determinarea următoarei opriri provenite de la panourile de comandă exterior, și împreună cu oprirea determinată de prima metodă, se va calcula oprirea finală la care se va opri liftul.
De asemenea, conține și o metodă ”Start”, prin care primește o comandă nouă, iar dacă liftul este în starea de repaus, acesta va fi pornit. O altă metodă este RunNoty folosită de clasa DoorsController, pentru a primi o notificare că ușile au fost închide și liftul poate porni în siguranță. Conține și o metodă Stop prin care se poate opri în siguranță și ”natural” acest fir de execuție.
Clasa funcționează în strânsă legătură cu clasele Controller, Elevator și Dir. Ea primește noile cereri provenite de la ambele panouri de control prin intermediul controlerului asignat acelui lift, și în cazul în care liftul este în modul de repaus, acesta va calcula direcția în care trebuie să meargă și etajul la care trebuie să oprească, dacă nu, va recalcula o posibilă altă oprire și va decide dacă poate sau nu să o ia în considerare. Folosește detalii colectate de la clasa Controller cât și de la clasa Elevator, cum ar fi: direcția de mers, poziția liftului pe interfața grafică, viteza de mișcare.
În momentul în care liftul se oprește la un etaj, se va trimite o notificare către controlerul de lift, ce va șterge din memorie cererile interne și externe pentru acel etaj și notificarea acestui eveniment pe interfața grafică și va notifica controlerul de operare a ușilor pentru deschiderea, iar mai apoi închiderea acestora. Când ușile s-au închis, se primește o notificare de la controlerul ușilor prin apelarea metodei notify(), și se va relua ciclul de calculare a opririi și mișcarea liftului la acel etaj. În cazul în care nu mai există nici o altă oprire, firul de execuție va apela metoda wait() și va intra în ”repaus”.
Această clasă conține următoarele obiecte importante:
lSyncStop, lSyncStart, lQueueSync, reprezentând obiecte de tip Boolean, folosite pentru sincronizarea metodelor GetNextStop() și Start(), iar ce-l de-al treilea este folosit pentru cazul în care liftul are deja o oprire stabilită, dar între timp apare o altă cerere iar după recalcularea opririi, următoarea ar fi la un etaj intermediar poziției curente a liftului și oprirea inițială, fiind necesară o sincronizare pentru stabilirea posibilității liftului de alegere a noii opriri
lElev, de tip Elevator, este folosit pentru setarea următoarei opriri a liftului ce va apărea pe interfața grafică, cât și pentru ”mișcarea” acestuia prin modificarea coordonatelor x și y
lCtrl, un obiect de tip Controller, utilizat pentru citirea de la controler a etajului la care se află liftul, direcției de mers, a cererilor de la diferite etaje cât și a celor pentru anumite etaje, și a stării ușilor liftului; toate aceste variabile fiind folosite pentru calcularea opririlor și controlul liftului
Clasa Dir
Reprezintă o clasă mică fără oarecare funcționalități, însă rolul și importanța ei în funcționalitatea aplicației este foarte mare, fără de care algoritmul nu ar mai funcționa. Utilizarea ei reduce semnificativ complexitatea codului și algoritmului, implementând unele metode foarte folositoare în: determinarea liftului pentru deservirea unei comenzi, determinarea direcțiilor lifturilor cât și a opririlor acestora. Scopul acestei clase pleacă de la necesitatea unui mod de a reprezenta direcțiile pentru care s-a solicitat un lift, și memorarea acestora într-un mod foarte accesibil și concis. De asemenea am dorit evitarea introducerii unui cod suplimentar necesar pentru procesarea acestor direcții, fie că sunt pentru cereri, fie pentru mers.
Tipul acestei clase este ”enum”, și este creată în pachetul ”Enums”. Avantajul acestor tipuri este că ele vor fi create doar o singură dată pe toată durata rulării programului, atunci când este nevoie de ele, și vor putea fi folosite de orice clasă.
Aceste tipuri pot fi personalizate ca și orice clasă, putând avea atât obiecte cât și proprietăți, diferențele majore fiind modul în care acestea se instanțiază, specificatorul de acces al constructorului, accesul la variabilele acestora și ciclul lor de viață.
Tabel 4.1 Diferențe între elementele de tip Class și Enum
În Tabelul 4.1 se poate observa modul în care un element de tip Class este instanțiat față de elementul de tip enum, și modul constructorilor. Tipurile enum trebuie să aibă întotdeauna obiectele ce vor fi instanțiate, cât și argumentele acestora (dacă există), declarate în interiurul clasei enum. În momentul în care se dorește utilizarea unei variabile de tip enum, se scrie numele clasei enum urmat de caracterul ”.” după care va urma numele acelei variabile. În acea clipă, dacă acea variabila nu a fost deja instanțiată se va crea, iar dacă deja există, va fi returnată. Avantajul acestor clase este că, întotdeauna va exista o singură instanță a fiecărei variabile (numite și Singleton, din engl.), și că pot fi coniderate ca și proprietăți primitive de tip int sau boolean, spre exemplu, în instrucțiuni if, switch, for, while etc. Spre deosebire de aceste tipuri primitive, valoarea lor fiind acea variabilă, de exemplu: element de tip int cu valoarea 1; element de tip enum cu valoarea x care mai poate avea în spatele ei o valoare reală 2 sau un obiect.
În cadrul aplicației, această clasă va instanția următoarele 4 proprietăți:
up, cu valoarea reală ”1”, semnificând direcția jos
down, cu valoarea reală ”5”, semnificând direcția sus
na, cu valoarea reală ”0”, semnificând nici o direcție
both, cu valoarea reală ”-1”, semnificând ambele direcții
Clasa conține următoarele metode:
”toByte()” ce va returna valoarea reală a proprietății
”Switch()” ce va returna ”up” în cazul unei proprietăți ”down” și ”down” în cazul proprietății ”up”
”HasDir” primește un argument de tip Dir pe care il va compara cu valoarea proprietății curente, în cazul în care cele două sunt de aceiași valoare, va returna true, și false în caz contrar
”MergeDir” primește un argument de tip Dir pe care il va concatena cu valoarea actuală a proprietății, și va returna noua proprietate rezultată după cum urmează:
dacă valoarea este ”both” sau valoarea este ”up” și argumentul ”down” sau valoarea este ”down” și argumentul ”up” va returna ”both”
dacă valoarea este egală cu argumentul sau valoarea este ”na”, va returna valoarea argumentului
Această clasă, împreună cu funcționalitățile sale, este folosită în cele mai importante componente ale aplicației
Modul de implementare a funcționării aplicației
Întreg controlul aplicației este bazat pe evenimente și stări, în care stările viitoare depind de starea în care s-a aflat, sau se află sistemul. Acesta din urmă poate fi considerat ca și unul cu evenimente discrete, deoarece fiecare acțiune a acestuia este influențată în cea mai mare parte de comenzile date de utilizator, ce vor fi ”filtrate” prin logica sistemului rezultând acțiunea de control al lifturilor sistemului.
În continuare se prezintă implementarea logicii folosite și funcționarea ei.
Inițializarea interfeței grafice și instanțierea claselor
Când se pornește prima dată aplicația, se va inițializa fereastra grafică având o dimensiune fixă de 800×700 pixeli, fără posibilitate de redimensionare. Se creează obiectele de tip Label folosite pentru descrierea unor instrucțiuni și afișarea încărcăturii și urmatoarei opriri a fiecărui lift în parte, specificând poziția fiecărui obiect în parte pe interfața grafică.
În continuare se prezintă obiectele create în această ordine:
două obiecte de tip Elevator cu următorii parametrii
coordonatele x și y în componenta de desenare, reprezentând poziția inițială la etajul 0: lift 1: x = 70, y = 561; lift 2: x = 280, y = 561
dimensiunile acestora fiind aceleași: 50×100 pixeli
fiecare lift având un ID unic: 0, respectiv 1
încărcătura maximă, fiind calculată în număr de persoane, de 12
un obiect de tip Painter ce primește ca și argument cele două obiecte Elevator
în cadrul constructorului acestei clase, se instanțiază și un fir de execuție de tip PainterWorker primind ca și argument obiectul Painter, și firul va fi pornit imediat dupa instanțierea lui
un obiect de tip Coordinator ce primește ca și argumente cele două obiecte de tip Elevator
la rândul lui, obiectul Coordinator va instanția două obiecte de tip Controller ce primesc ca și argument un obiect de tip Elevator, care la rândul lor vor instanția câte un obiect de tip DoorsController ce vor primi ca și argument un obiect Elevator și unul Controller
dupa crearea fiecărui obiect Controller, se va crea legătura dintre Coordinator si Controller, moment în care se va instanția câte un obiect ElevatorWorker în fiecare obiect Controller
un număr de 8 obiecte OutsidePanel reprezentând butoanele de chemare a liftului de la fiecare etaj
primesc ca și argument direcția pentru care se face chemarea liftului, etajul la care se află, și obiectul de tip Coordinator
la crearea acestor obiecte, se adaugă ca și obiect de ”ascultare” a evenimentelor produse de fiecare buton, coordonatorul primit ca și argument
un obiect de tip Choice folosit pentru selectarea liftului ce va fi comandat de panoul din interiorul liftului
pentru detectarea schimbării selecției se folosește un obiect de tip ItemListener care, în momentul în care se face o nouă selecție va apela o metodă de selectare a liftului din clasa Coordinator, trimițând ca și argument ID-ul liftului selectat
În ultimă instanță, se adaugă toate obiectele vizuale pe interfața grafică, după care aplicația poate fi folosită de către utilizator.
Figura 4.12 Interfața grafică a simulatorului de lifturi
În Figura 4.12 este prezentată interfața grafică a aplicației, specificându-se fiecare element în parte după cum urmează:
indicator de încărcare al liftului exprimată în procente (%)
indicator al etajului la care liftul va opri
buton de chemare a liftului la etajul respectiv
selectare a liftului ce va fi controlat de butoanele de pe panoul intern
panoul din interiorul liftului împreună cu butoanele de selectare a etajelor și cele pentru modul ALARMA și MANUAL
reprezentarea lifturilor
indicarea etajului la care se află liftul și direcția de mers (d = jos, u = sus, – = nu se mișcă)
Primirea și procesarea comenzilor de la butoanele de pe panourile externe
În momentul în care se apasă un buton de chemare a liftului, această acțiune va fi preluată de metoda ”actionPerformed” din clasa Coordinator. Această metoda este sincronizată, ceea ce înseamnă ca poate fi apelată doar de un singur fir de execuție la un moment dat, ceea ce garantează funcționarea corectă a algoritmului de alegere a liftului și de procesare a acelei comenzi, fără ca starea inițială să fie modificată de o altă cerere. Celelalte cereri ce vor apărea cât timp există o cerere în curs de procesare, vor fi puse într-o coadă de așteptare FIFO de către mașina virtuală Java. Această funcție primește ca și argument un obiect ActionEvent, cu ajutorul căruia se poate accesa butonul de la care a pornit acțiunea, și se va extrage din acel buton etajul și direcția pentru care s-a făcut cererea. În cazul în care mai există o cerere de la acel etaj pentru aceiași direcție, cererea va fi ignorată și sistemul va aștepta o altă cerere. Dacă nu există o cerere identică, se va apela metoda ”ChooseElevator” și se va da ca și argument etajul și direcția cererii, și în funcție de răspunsul primit se procedează astfel:
dacă răspunsul este ”true” înseamnă ca a fost găsit un lift capabil să deservească acea cerere și a acceptat-o; în acest moment se va memora cererea și se va semnaliza pe interfața grafică acest lucru prin colorarea butonului în culoarea roșie
dacă raspunsul este ”false” înseamnă că nu a fost găsit un lift sau a fost găsit dar acea comandă nu a putut fi acceptată; în cazul acesta sistemul nu va mai continua cu procesarea comenzii și va aștepta alte cereri
După acest pas, dacă a fost găsit un lift, mai exact un controler al unui lift pentru procesarea acelei comenzi, clasa Coordinator va apela metoda ”GoToFloor” trimițând ca și argumente etajul și direcția cererii.
În cadrul clasei Controller, la primirea cererii se va verifica:
dacă etajul de la care provine cererea este același cu etajul la care se află liftul, atunci:
se verifică dacă liftul nu este plin și va apela metoda ”OpenDoors” din clasa DoorsController pentru simularea deschiderii ușilor, în caz contrar cererea va fi ignorată și răspunsul returnat către Coordinator va fi ”false”;
după deschiderea ușilor se va încărca și descărca liftul cu ”persoane”, după care se va returna răspunsul ”false” deoarece cererea a fost deja procesată prin deschiderea ușilor și nu necesită mișcarea liftului
dacă cererea provine de la alt etaj, se va memora cererea pentru etajul respectiv și va fi trimisă o notificare către clasa ElevatorWorker prin metoda ”Start” specificând etajul la care a apărut acea cerere
La primirea cererii în cadrul clasei ElevatorWorker, există două cazuri:
în cazul în care liftul este în modul de repaus și nu procerează nicio cerere se va calcula direcția în care va merge liftul pentru cererea primită, iar dacă ușile sunt închise, se apelează metoda de calculare a următoarei opriri, și dacă a fost găsită o cerere liftul va porni spre acel etaj
se va seta pe interfața grafică direcția de mers și etajul la care se află liftul, iar mișcarea efectivă se va face într-o buclă while, fiind executată până când etajul la care se află liftul este același cu etajul la care a fost calculată oprirea
la oprirea liftului la un etaj, se va apela metoda ”OpenDoors” din clasa DoorsController și acest fir de execuție va intra în așteptare apelând metoda ”wait()”
după închiderea ușilor, firul de execuție ElevatorWorker va primi o notificare prin metoda ”RunNoty”, și implicit ”notify()”, după care va căuta o altă oprire; dacă a fost găsită o altă oprire, se va începe din nou ciclul descris mai sus, dacă nu, va intra înapoi în așteptare reapelând metoda wait, până când o să apară o altă cerere ce va ”trezi” firul de execuție reluând acest ciclu
dacă liftul este deja în mișcare pentru procesarea unei comenzi, se apelează direct metoda de recalculare a următoarei opriri, luând în calcul și această cerere nouă:
dacă după recalcularea opririi, cea rezultată este aceiași ca și cea inițială, funcționarea liftului nu este afectată sau schimbată, acea cerere fiind memorată și deservită cu o altă ocazie posibilă
dacă oprirea rezultată după recalcularea acesteia, se află la un etaj intermediar între cel la care se află liftul și cel la care se afla oprirea inițială, asceastă schimbare a opriri se va realiza cu ajutorul sincronizării pentru evitarea cazului în care se setează noua oprire dar, liftul deja a trecut de acel etaj, dând sistemul peste cap
Când un lift se oprește se apelează metoda ”WorkerStopped” din clasa Controller, ce șterge cererile pentru acel etaj, deschide ușile liftului, simulează încărcarea și descărcarea acestuia, indică pe interfața grafică faptul că o cerere a fost ștearsă, atât pe panoul interior cât și cel exterior, și trimite o notificare clasei Coordinator, ce va șterge la rândul ei cererile pentru acel etaj.
Primirea și procesarea comenzilor de la butoanele de pe panourile interne
La apăsarea unui buton de selectare a etajului pe panoul de control intern, acțiunea va fi preluată de clasa ”InsidePanel” prin metoda sincronizată ”actionPerformed” ce primește ca și argument un obiect ActionEvent din care se extrage numărul etajului asignat. În continuare se apelează metoda ”SetFloor” din clasa Coordinator trimițând și numărul etajului selectat. Mai departe se trimite acea cerere către controlerul liftului selectat pentru operarea acelui panou, prin metoda ”AddFloorReq”. În cazul în care liftul este în modul ”ALARMA”, cererea va fi ignorată.
Dacă liftul se află la același etaj cu cel selectat de pe buton, se vor deschide ușile și se va încărca liftul, returnând un răspuns ”false” indicând ca acea cerere a fost procesată și nu mai este necesară memorarea ei.
Dacă liftul se află la un etaj diferit, se memorează cererea și trimisă mai departe clasei ElevatorWorker prin metoda ”Start”, unde se va aplica aceiași funcționalitate descrisă mai sus.
Implementarea logicii de control
În această aplicație există două tipuri de logică folosite: o logică Fuzzy folosită pentru selectarea liftului la primirea unei comenzi de la panoul de control extern, și o logică folosită pentru calcularea următoare opriri a liftului.
În cazul unei egalități la alegerea unui lift, va fi ales acela cu prioritate mai mare, adică, în această aplicație, liftul din partea stângă sau acela cu ID-ul 0.
Pentru realizarea logicii Fuzzy este necesară luarea în calcul a următoarelor aspecte:
Încărcătura liftului
Timpul necesar al liftului de a ajunge la un anumit etaj
Numărul total de opriri al liftului
Cazurile și logica de alegere al unui lift
Această logică este implementată și folosită de clasa Coordinator în momentul în care apare o cerere de la butoanele de pe panoul extern, și funcționează astfel:
Dacă ambele lifturi sunt în repaus se va alege liftul cel mai apropait de etajul la care a fost solicitat
Dacă un lift este în modul ALARMA sau MANUAL, întotdeauna va fi ales celălalt lift
Dacă ambele lifturi se află în modul ALARMA sau MANUAL, cererile vor fi ignorate
Un lift se află în repaus iar celălalt lift merge pe direcția sus:
dacă etajul cererii este deasupra etajului la care se află liftul în mișcare: dacă direcția de mers a cereri este aceiași cu cea a liftului în mișcare, acel lift va fi selectat; dacă direcția de mers diferă, va fi selectat liftul aflat în repaus
dacă etajul cererii este sub etajul curent al liftului în mișcare, va fi selectat liftul aflat în repaus
Un lift se află în repaus iar celălalt lift merge pe direcția jos:
dacă etajul cererii se află sub etajul la care se află liftul în mișcare: dacă direcția de mers a cereri este aceiași cu cea a liftului în mișcare, acel lift va fi selectat; dacă direcția de mers diferă, va fi selectat liftul în starea de repaus
dacă etajul cererii se află deasupra etajului curent al liftului în mișcare, va fi selectat liftul în starea de repaus
Ambele lifturi sunt pe aceiași direcție de mers:
dacă lifturile merg în jos, direcția cererii este tot jos și cel puțin un lift se află deasupra etajului cererii, va fi ales liftul cel mai apropiat de acel etaj, și care are încărcătura cea mai mică
dacă lifturile merg în jos și direcția cererii este jos, dar niciun lift nu este deasupra etajului cererii, se va folosi logica fuzzy pentru alegerea liftului, luând în calcul timpul necesar până va ajunge la acel etaj și încărcătura acestuia
dacă lifturile merg în jos și direcția cererii este sus, se va folosi logica fuzzy pentru alegerea liftului, luând în calcul timpul necesar până va ajunge la acel etaj și încărcătura acestuia
Lifturile merg în sensuri diferite
dacă lifturile merg în sus, direcția cererii este tot sus și cel puțin un lift se află sub etajul cererii, va fi ales liftul cel mai apropiat de acel etaj, și care are încărcătura cea mai mică
dacă lifturile merg în sus și direcția cererii este sus, dar niciun lift nu este sub etajul cererii, se va folosi logica fuzzy pentru alegerea liftului, luând în calcul timpul necesar până va ajunge la acel etaj și încărcătura acestuia
dacă lifturile merg în sus și direcția cererii este jos, se va alege liftul care va ajunge cel mai rapid la etajul cererii
dacă un lift merge în jos și unul în sus, sau invers, și direcția cererii este pentru sus și liftul care merge în sus este sub etajul cererii și nu este plin, va fi ales acel lift; dacă acel lift este plin va fi ales celălalt lift, sau dacă acel lift este deasupra etajului cererii, se va folosi logica fuzzy pentru alegerea liftului, luând în calcul timpul necesar până va ajunge la acel etaj și încărcătura acestuia
dacă un lift merge în jos și unul în sus, sau invers, și direcția cererii este pentru jos și liftul care merge în jos este deasupra etajului cererii și nu este plin, va fi ales acel lift; dacă acel lift este plin va fi ales celălalt lift, sau dacă acel lift este sub etajul cererii, se va folosi logica fuzzy pentru alegerea liftului, luând în calcul timpul necesar până va ajunge la acel etaj și încărcătura acestuia
Tabel 4.2 Logica Fuzzy bazată pe încărcătura liftului
În Tabelul 4.2 este prezentată logica Fuzzy folosită pentru alegerea unui lift în funcție de încărcătura acestuia. Cazurile de alegere sunt: L – low (încărcătură 0-33%), M – medium (încărcătură 34-66%), H – high (încărcătură 67-100%). Lift1 reprezintă liftul din partea stângă cu ID-ul 0, Lift2 reprezintă liftul din partea dreaptă cu ID-ul 1.
Tabel 4.3 Logica Fuzzy bazată pe numărul de opriri al lifturilor
În Tabelul 4.3 este descrisă logica Fuzzy și modul în care se alege un lift în funcție de numărul de opriri al acestuia, putând, de asemena, reprezenta și timpul necesar deservirii unor comenzi până când acel lift va ajunge la un anumit etaj. Cazurile de alegere sunt următoarele: L – low (1 oprire), L-M – low-medium (2 opriri), M – medium (3 opriri), M-H – medium-high (4 opriri), H – high (≥ 5 opriri). Lift1 reprezintă liftul din partea stângă cu ID-ul 0, Lift2 reprezintă liftul din partea dreaptă cu ID-ul 1.
Logica de alegere a următoarei opriri al unui lift
Această logică este implementată și folosită de către clasa ElevatorWorker în momentul în care apare o nouă cerere de oprire sau când liftul s-a oprit la un etaj pentru căutarea următoarei destinații. În continuare este descris modul în care funcționează această logică și pașii urmați:
dacă liftul nu este plin și modul manual nu este activat, se pornește de la pozitia curentă a liftului și se caută în ambele direcții (sus și jos) cererile de opriri de la panourile externe; dacă liftul este plin sau modul manual este activat, se trece direct la pasul 4.
dacă s-a găsit pe direcția sus, se memorează acest fapt și dacă acea cerere are aceiași direcție cu cea a liftului se memorează acel etaj ca și oprire pentru nivelul superior liftului; dacă direcția este diferită, se va memora oprirea de la etajul cel mai superior
dacă s-a găsit pe direcția jos, se memorează acest fapt, iar dacă acea cerere este pentru aceiași direcție ca și cea a liftului, se memorează acel etaj ca și oprire pentru nivelul inferior liftului; dacă direcția este diferită, se va memora oprirea de la etajul ce mai incerior
se caută următorul etaj pentru care a fost setată o oprire de la panoul de comandă intern, pornind de la poziția curentă a liftului, și căutând pe direcția de mers a acestuia; dacă s-a găsit o astfel de oprire, se memorează acel etaj
dacă nu avem nici o oprire de la panourile externe, dar exisă o oprire de la panoul intern, următoarea destinație a liftului va fi etajul acelei opriri
dacă direcția liftului este sus și există o oprire la un etaj superior liftului de la panoul extern pe aceiași direcție, se verifică dacă există o cerere din interiorul liftului pentru un etaj intermediar poziției curente a liftului și oprirea etajului superior: dacă există o astfel de cerere, acel etaj va fi următoarea destinație, dacă nu, următoarea destinație va fi acel etaj superior
dacă direcția liftului este jos și există o oprire la un etaj inferior liftului de la panoul extern pe aceiași direcție, se verifică dacă există o cerere din interiorul liftului pentru un etaj intermediar poziției curente a liftului și oprirea etajului inferior: dacă există o astfel de cerere, acel etaj va fi următoarea destinație, dacă nu, următoarea destinație va fi acel etaj inferior
dacă direcția de mers a liftului este sus și nu există nici o cerere de la un etaj superior de la panoul extern, se verifică dacă există o cerere de la panoul intern pentru un etaj superior poziției curente a liftului: dacă există, acel etaj va fi următoarea oprire, dacă nu, se schimbă direcția de mers a liftului și se repetă pașii 1-7 și 9
dacă direcția de mers a liftului este jos și nu există nici o cerere de la un etaj inferior de la panoul extern, se verifică dacă există o cerere de la panoul intern pentru un etaj inferior poziției curente a liftului: dacă există, acel etaj va fi următoarea oprire, dacă nu, se schimbă direcția de mers a liftului și se repetă pașii 1-8
dacă după executarea pașilor 1-9 se găsește o oprire:
dacă liftul este în repaus, se setază acea oprire ca și următoarea destinație și se pornește liftul
dacă liftul este în funcționare, se încearcă schimbarea următoarei destinații; în caz de eșec, acea oprire va fi deservită cu altă ocazie
dacă după executarea pașilor 1-9 nu se găsește nici o oprire, lliftul va intra în repaus până la apariția unei noi comenzi
Logica de încărcare și descărcare a lifturilor
Este o logică implementată și folosită în clasa Elevator, executată în momentul deschiderii ușilor liftului și se bazează pe tipul cererii ce a determinat deschiderea ușilor, adică cerere de la panoul intern sau extern. În momentul în care se apasă un buton de pe panoul de comandă intern, se memorează o cerere de încărcare a liftului pentru acel etaj respectiv, cânt este acționat un buton de pe panoul de comandă extern se memorează o cerere de descărcare a liftului la acel etaj.
În timpul executării se urmează următorii pași:
se verifică dacă pentru etajul la care s-a oprit există o cerere de descărcare și liftul este încărcat într-o anumită proporție; dacă aceste condiții sunt îndeplinite, se caută alte cereri de descărcare al acelui lift pentru alte etaje, dacă nu se găsește, liftul va fi descărcat complet, în caz contrar, se va descărca cu un număr generat aleatoriu
se verifică, de asemenea, dacă pentru etajul la care s-a oprit există cerere de încărcare și liftul nu este plin, moment în care liftul va fi încărcat cu un anumit procentaj generat aleatoriu
Modelarea aplicației folosind Rețele Petri de nivel înalt cu temporizare
Întreaga aplicație va fi modelată pe componente folosind rețele Petri de nivel înalt cu temporizare, după cum urmează: componenta ce primește cererile de lift și selectează un lift pentru deservirea acestora și componenta de calculare a următoarei opriri și mișcare a liftului.
În aceste modele, tranzițiile reprezință diferite acțiuni executate de către sistem, iar locațiile reprezintă stări ale acestuia după executarea unor instrucțiuni.
Modelarea acestor componente și simularea lor s-a realizat utilizând mediul de simulare HPSim.
Figura 4.13 Modelarea componentei de primire a cererilor și alegere a liftului
Tabel 4.4 Explicația tranzițiilor și locațiilor din modelul componentei de primire și procesare a cererilor
Figura 4.14 Modelarea componentei de calculare a următoarei opriri și mișcare a liftului
Tabel 4.5 Explicația tranzițiilor și locațiilor din modelul componentei de calculare a următoarei opriri și mișcare a liftului
În Figura 4.13 este prezentat modelul rețelei Petri de nivel înalt, ce reprezintă componenta de primire a comenzilor și alegere a unui lift pentru acestea. Tranziția T17 este cea care va decide ce lift va fi ales, sau în ce locație va produce un jeton, bazându-se pe tipul jetoanelor primite și starea sistemului.
În Tabelul 4.4 sunt explicate semnificațiile tranzițiilor și locațiilor din Figura 4.13.
În Figura 4.14 este s-a modelat, utilizând rețele Petri de nivel înalt cu temporizări, componenta de calculare a opririlor și mișcare a liftului. Tranziția T5 va necesita un număr de 10 unități de timp pentru procesarea unei comenzi.
În Tabelul 4.5 sunt explicate semnificațiile tranzițiilor și locațiilor din Figura 4.14.
Testare și validare
În acest capitol s-a realizat testarea funcționării generale a aplicației, a afișării corecte a stării sitemului pe interfața grafică, cât și al algoritmilor folosiți în controlul lifturilor. La sfârșitul testării, s-au validat rezultatele obținute pentru fiecare test în parte și s-a verificat dacă sunt în concordanță cu rezultatele așteptate.
Pentru testarea interfeței grafice, se acționează butoanele de control și se verifică dacă acestea sunt funcționale și răspund conform stării sistemului. Se verifică afișarea corectă a mișcării lifturilor, cât și a stării acestora.
Testarea funcționării algoritmilor și controlului lifturilor se face prin afișarea unor mesaje în consola mediului de dezvoltare Eclipse, ce descriu starea și rezultatul fiecărui pas executat de la momentul apăsării unui buton pe interfața grafică până la momentul în care cererea a fost procesată.
Testarea aplicației
Pentru testarea modurilor ”MANUAL” și ”ALARMA” se setează pe rând cele două lifturi în fiecare mod, după care ambele într-un mod aleatoriu și se acționează un buton de pe panoul extern. Rezultatul obținut la alegerea unui lift pentru modul ”ALARMA” se poate observa în Figura 5.1, care este același și pentru Lift2, cât și pentru modul ”MANUAL”.
Figura 5.1 Testarea modului ALARMA pentru Lift1
În Figura 5.2 se poate observa rezultatul testului pentru modul ”MANUAL” la algerea următoarei opriri. În acest mod, toate cererile de la panoul extern vor fi ignorate, fiind procesate doar cele de la panoul intern. Același rezultat se va obține și pentru testarea acestui mod la Liftul1.
Figura 5.2 Testarea modului MANUAL pentru Lift2
Figura 5.3 Testarea algoritmului de alegere a lifturilor în funcție de starea lor curentă
În Figura 5.3 sunt prezentate mesajele aplicației din diferite componente de control, rezultate ca urmare a apăsării butoanelor de pe panourile de control și execuția algoritmului de alegere a lifturilor, ce se bazează doar pe starea curentă a lifturilor, și cel de alegere a următoarei opriri.
Figura 5.4 Interfața grafică a aplicației și reprezentarea stării sistemului
În Figura 5.4 sunt surprinse butoanele ambelor panouri de comandă, în funcție de starea lor (există sau nu o comandă pentru acestea), starea lifturilor (plin, uși deschise etc.), indicarea încărcăturii lifturilor, următoarea oprire a acestora din urmă.
Figura 5.5 Validarea utilizării panoului intern
După cum se poate observa în Figura 5.5, dacă nu este selectat niciun lift ce va fi operat de panoul intern, se va afișa un mesaj de eroare în acest sens.
Validarea rezultatelor
În urma testării funcționalităților principale, și a mesajelor generate de aplicație în timpul executării algoritmilor de control și calcul, se poate spune că aplicația funcționează conform cerințelor și în limita posibilităților oferite de aceasta.
Ambele moduri de funcționare (MANUAL și ALARMA) al lifturilor se comportă potrivit cerințelor, un lift putând să ignore doar comenzile de la panourile exterioare cât și toate comenzile apărute, în funcție de caz.
Metoda de calculare a următoarei opriri funcționează precum așteptărilor, luând în calcul atât nivelul de încărcare al liftului cât și modurile de funcționare ale acestuia. Întotdeauna se va căuta o oprire ce poate fi deservită utilizând direcția curentă de mers a liftului, și schimbarea acesteia doar când nu mai există alte cereri pentru acea direcție de mers.
În cazult algoritmului de alegere a liftului pentru o comandă de la panoul de comandă extern, rezultatele nu vor fi întotdeauna cele mai bune sau cele așteptate, deoarece starea sistemului depinde foarte mult de eventualele cereri ce pot apărea după executarea algoritmului, cât și de imposibilitatea prezicerii nivelului de încărcare sau descărcare a liftului după ce acesta s-a oprit la un etaj. La momentul execuției acestui algoritm, se va lua în calcul doar starea sistemului din acel moment, așadar, dacă încărcătura lifturilor este, spre exemplu, 0 și vor apărea un anumit număr de cereri înainte ca acest număr să se modifice, în cadrul calculării cazurilor de alegere a liftului se va lua în calcul întotdeauna acea valoare 0, ceea ce face ca uneori algoritmul să nu funcționeze conform așteptărilor și cerințelor.
Interfața grafică arată în permanență starea curentă a sistemului, prin colorarea lifturilor și a butoanelor de comandă, și indicarea următoarei opriri și a nivelului de încărcare a fiecărui lift în parte. Datorită utilizării firelor de execuție separate pentru rularea diferitelor componente în paralel, interfața grafică nu prezintă momente de blocaje sau înghețare a comenzilor, fiind în permanență utilizabilă și funcțională. Butoanele panourilor, cât și celelalte indicatoare de stare ale sistemului, sunt în permanență actualizate în funcție de starea acestuia din urmă.
Mișcarea lifturilor este realizată conform indicatoarelor de stare ale acestora: următoarea oprire, nivelul de încărcare, etajul curent, direcția de mers și modul de funcționare.
Simularea rețelelor Petri în mediul de simulare HPSim a rezultat fără apariția unui blocaj în model.
Ca și concluzie, funcționarea aplicației cât și al algoritmilor de control se încadrează în specificațiile aplicației, putând fi aduse îmbunătățiri ale acestora într-o versiune ulterioară.
Concluzii
Concluzii finale
În prezenta lucrare s-a dorit proiectarea și implementarea unui algoritm de control optiml pentru un sistem de lifturi, ce poate fi modelat utilizând rețele Petri de nivel înalt temporizate. Pentru utilizarea, simularea și testarea acestui algoritm, s-a proiectat o aplicație Java cu o interfață grafică, unde se putea simula funcționarea unui sistem de lifturi virtual, folosind algoritmul conceput în această lucrare. Pe parcursul acesteia din urmă, s-au realizat diferite diagrame UML ce reflectă structura, funcționarea și comunicarea componentelor sistemului, cât și descrierea claselor utilizate pentru implementarea aplicației. În final, algoritmul a fost modelat și reprezentat folosind rețelele Petri de nivel înalt, după care s-a realizat testarea funcționalității aplicației și a algoritmilor de control împreună cu validarea rezultatelor obținute.
Contribuții personale
Pentru dezvoltarea și implementarea aplicației am folosit în principal mediul de dezvoltare Eclipse în care am creat propriile mele clase, unele moștenind și implementând clase din pachetul Java precum ”Thread” și ”ActionListener”. Pentru realizarea diagramelor am folosit aplicația StarUML, iar pentru modelarea sistemului folosind rețele Petri am folosit simulatorul HPSim. Totalitatea codului scris în clasele proiectului, proiectarea și implementarea algoritmului, design-ul interfeței grafice cât și funcționalitățile oferite de aceasta, precum și crearea diagramelor UML și a modelării rețelelor Petri reprezintă contribuția mea în dezvoltarea acestei aplicații, cu excepția folosirii unor clase și pachete din librăriile standard aflate în pachetul JDK. Modul în care funcționează algoritmul a fost conceput de către mine, în care am încercat să rezolv probleme pe care le-am întâlnit în realitate la diferite sisteme de lifturi.
Posibile dezvoltări ulterioare
Proiectarea și dezvoltarea acestui proiect, a presupus crearea ”scheletului” aplicației ce oferă funcționalitățile de bază, cât și a algoritmilor de control principali ce sunt necesari funcționării acesteia. De asemenea, au fost implementate și unele elemente extra, pentru setarea diferitelor moduri de funcționare a lifturilor, cât și a unor indicatoare ale stării sistemului în timp real.
Cu toate acestea, algoritmii folosiți pot fi îmbunătățiți și optimizați pentru o mai bună performanță a acestora, realizând același rezultat utilizând resurse mai puține, sau pot fi reproiectați în scopul de a oferi funcționalități mai noi și mai performante.
Interfața grafică poate fi extinsă pentru adăugarea unor noi posibilități de interacționare cu sistemul sau de moficare a stării acestuia, pot fi extinse numărul de lifturi ce pot fi controlate, împreună cu algoritmul de control pentru a suporta acel număr de controlere pentru ficare lift, mărirea numărului de etaje ale sistemului.
O propunere pentru un proiect viitor, ar putea fi reprezentată de transformarea structurii acestei aplicații, într-un sistem distribuit, implicând și controlul acestuia de la distanță. Deoarece în ziua de azi, tot mai multe procese și sisteme utilizate în diferite domenii au abilitatea de a putea fi controlate de la distanță prin Internet fie folosind un laptop, un computer, o tabletă, sau chiar un telefon cu sistem de operare Android sau iOS. Așadar, aplicația dezvoltată în acest proiect, ar putea fi extinsă pentru a suporta conexiunea la internet și să fie transformată într-o componentă server, la care să se poată conecta o aplicație client dezvoltată pentru un anume sistem de operare. În această aplicație client, ar fi posibilă monitorizarea în timp real a sistemului de lifturi, și în funcție de nivelul de acces al utilizatorului, se poate sau nu suprascrie comenzile liftului, controlarea acestuia, introducerea unor comenzi noi, totul făcându-se de la distanță prin intermediun Internetului sau orice altă conexiune.
De asemenea, se poate propune implementarea acestui algoritm de control pe un microcontroler, și construirea fizică unui sistem de lifturi în miniatură pentru a simula funcționalitatea acelui algoritm și aducerea proiectului mai aproape de realitate. Bineînțeles, se poate opta, în acest caz, și pentru implementarea fizică a sistemului ca și unul distribuit, folosind module server-client pentru controlul la distanță.
Bibliografie
[1] http://www.sfgate.com/homeandgarden/article/Louis-XV-elevator-king-2658083.php
[2] http://www.britannica.com/EBchecked/topic/434691/Elisha-Graves-Otis
[3] http://www.mitsubishielectric.com/elevator/overview/elevators/history.html
[4] http://elevation.wikia.com/wiki/Elevator_control_system
[5] Irmak, E. ; Colak, I. ; Kaplan, O. ; Kose, A., ”Development of a real time monitoring and control system for PLC based elevator”, Power Electronics and Applications (EPE 2011) Proceedings of the 2011-14th European Conference on Publication, 2011
[6] Xiaoling Yang ; Qunxiong Zhu ; Hong Xu, ”Design and Practice of an Elevator Control System Based on PLC”, Power Electronics and Intelligent Transportation System PEITS '08 Workshop, 2008
[7] Atmel’s Self-Programming Flash Microcontrollers, http://www.atmel.com/Images/doc2464.pdf
[8] Becker, A., ”Microcontroller based elevator controlling system”, Electronics Technology 30th International Spring Seminar, 2007
[9] Huseinbegovic, S. ; Kreso, S. ; Tanovic, O., ”Development of a distributed elevator control system based on the microcontroller PIC 18F458”, Computational Technologies in Electrical and Electronics Engineering (SIBIRCON) 2010 IEEE Region 8 International Conference, 2010
[10] Zhu Deven ; Wu Chengdong, ”Fuzzy control of group elevators”, TENCON '93. Proceedings. Computer, Communication, Control and Power Engineering IEEE Region 10 Conference, 1993
[11] Gu deying ; Yan dongmei, ”Study on Fuzzy Algorithm of Elevator Group Control System”, Challenges in Environmental Science and Computer Engineering (CESCE) IEEE International Conference, 2010
[12] Jing Wang ; Xiaomu Mu ; Jun Xu ; Shuning Wang, ”Design and optimization of dispatching rules for elevator group control aiming at energy saving”, Information Science and Technology (ICIST) IEEE International Conference, 2012
[13] Zhu Dewen ; Jiang Li ; Zhou Yuwen ; Shan Guanghui ; He Kai, ”Modern elevator group supervisory control systems and neural networks technique”, Intelligent Processing Systems ICIPS '97. IEEE International Conference, 1997
[14] http://www.otisworldwide.com/pdf/AboutElevators.pdf
[15] Thomas H. Cormen, ”Algorithms Unlocked”, The MIT Press, 2013
[16] Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, Clifford Stein, ”Introduction to Algorithms, Third Edition”, The MIT Press, 2009
[17] Tyson Macaulay, Bryan Singer, ”Cybersecurity for Industrial Control Systems: SCADA, DCS, PLC, HMI, and SIS”, Auerbach Publications, 2012
[18] Robert Radvanovsky and Jacob Brodsky, ”Handbook of SCADA/Control Systems Security”, Auerbach Publications, 2013
[19] Herbert Schildt, ”Java: The Complete Reference”, Oracle Press, 2011
[20] Michael T. Goodrich, Roberto Tamassia, ”Data Structures and Algorithms in Java”, John Wiley & Sons, 2001
[21] Partha Kuchana, ”Software Architecture Design Patterns in Java”, Auerbach Publications, 2004
[22] Wil van der Aalst, Kees van Hee, ”Workflow Management: Models, Methods, and Systems”, The MIT Press, 2002
[23] Tim Weilkiens, ” Systems Engineering with SysML/UML: Modeling, Analysis, Design”, Morgan Kaufmann Publishers, 2007
[24] Kevin Lano (ed), ”UML 2 Semantics and Applications”, John Wiley & Sons, 2009
[25] Bruce Powel Douglass, ”Real-Time UML Workshop for Embedded Systems, Second Edition”, Newnes, 2014
Acronime
THLPN – Timed High-Level Petri Nets (Rețele Petri de nivel înalt cu temporizare)
UML – Unified Modeling Language (Limbaj standard pentru descrierea de modele)
PLC – Programmable Logic Controller (Controler Programabil Logic)
ROM – Read Only Memory (Memorie ce permite doar citirea)
RAM – Random Access Memory (Memorie cu acces aleator)
USB – Universal Serial Bus (Magistrală Serială Universală)
CAN – Convertor Analogic-Numeric
CNA – Convertor Numeric-Analogic
LED – Light-Emitting Diode (Diodă Emițătoare de Lumină)
CAN – Controller Area Network (Magistrală serială pentru comunicarea microcontrolerelor)
SOF – Start of frame (Începutul mesajului)
RTR – Remote transmission request (Cererea de transmisie a mesajului)
CRC – Cyclic redundancy check (Control Redundant Ciclic)
ACK – Acknowledge (Recepționat)
EOF – End of frame (Sfârșitul mesajului)
SRAM – Static Random Access Memory (Memorie cu acces aleator statică)
EEPROM – Electrically Erasable Programmable Read-Only Memory
REM – Remote Elevator Monitoring (Monitorizarea liftului de la distanță)
J2SE – Java 2 Standard Edition
Java SE – Java Standard Edition
API – Application Programming Interface (Interfață de programare a unei aplicații)
POO – Programare Orientată pe Obiect
JDK – Java Development Kit (Set de instrumente Java pentru dezvoltare de aplicații)
iOS – iPhone Operating System (Sistem de operare dezvoltat de Apple Inc.
Surse ale figurilor prezente în această lucrare:
[*1]_http://en.wikipedia.org/wiki/File:Konrad_Kyeser,_Bellifortis,_Clm_30150,_Tafel_09,_Blatt_38v_(Ausschnitt).jpg
[*2] http://en.wikipedia.org/wiki/File:Elisha_OTIS_1853.jpg
[*3] http://en.wikipedia.org/wiki/File:OtisControl.jpg
[*4] http://elevation.wikia.com/wiki/File:Express_Lift_relay_logics_60s.jpg
[*5] http://elevation.wikia.com/wiki/File:Express_Lift_relay_logics_60s_(2).jpg
[*6] http://elevation.wikia.com/wiki/File:Schindler_elevator_floor_selector.jpg
Lista de figuri
Figura 2.1 Lift din secolul al XV-lea[*1] 5
Figura 2.2 Demonstrația sistemului de siguranță[*2] 6
Figura 2.3 Controler manual al liftului[*3] 7
Figura 2.4 Dulapul cu releele de control a liftului[*4][*5] 8
Figura 2.5 Selectorul de etaj[*6] 8
Figura 2.6 Schema semnalelor de control a PLC-ului 9
Figura 2.7 Schema de bază a unui microcontroler 11
Figura 2.8 Structura de control Fuzzy 12
Figura 2.9 Structura parțială a unei rețele neuronale 13
Figura 2.10 Modul de operare al sistemului REM 14
Figura 3.1 Reprezentarea unei Rețele Petri clasică 21
Figura 3.2 Rețea Petri de nivel înalt cu extensie ierarhică 23
Figura 3.3 Rețea Petri de nivel înalt cu extensie temporizată 23
Figura 3.4 Diagrama de componente ale aplicației 24
Figura 3.5 Diagrama Use Case 25
Figura 3.6 Diagrama claselor 26
Figura 3.7 Diagrama de obiecte 27
Figura 3.8 Diagrama de colaborare dintre obiecte 28
Figura 3.9 Diagrama de activități 29
Figura 3.10 Diagrama de secvențe 30
Figura 4.1 Crearea unui nou proiect Java 34
Figura 4.2 Crearea unei noi clase Java 34
Figura 4.3 Forma finală a proiectului după crearea claselor și pachetelor 35
Figura 4.4 Butoanele de chemare a liftului 36
Figura 4.5 Indicator de încărcare a lifturilor 36
Figura 4.6 Selectarea liftului ce va fi operat de panoul de control 36
Figura 4.7 Reprezentarea componentei desenate de către clasa Painter 37
Figura 4.8 Componența panoului din interior 39
Figura 4.9 Reprezentarea stării normale și a ușilor deschise 41
Figura 4.10 Reprezentarea stării unui lift încărcat la maxim 42
Figura 4.11 Reprezentarea modurilor ”ALARMA” și ”MANUAL” 42
Figura 4.12 Interfața grafică a simulatorului de lifturi 47
Figura 4.13 Modelarea componentei de primire a cererilor și alegere a liftului 53
Figura 4.14 Modelarea componentei de calculare a următoarei opriri și mișcare a liftului 54
Figura 5.1 Testarea modului ALARMA pentru Lift1 57
Figura 5.2 Testarea modului MANUAL pentru Lift2 58
Figura 5.3 Testarea algoritmului de alegere a lifturilor în funcție de starea lor curentă 58
Figura 5.4 Interfața grafică a aplicației și reprezentarea stării sistemului 59
Figura 5.5 Validarea utilizării panoului intern 59
Lista de tabele
Tabel 2.1 Structura standard a mesajelor pe magistrala serială CAN 11
Tabel 3.1 Evoluția versiunilor limbajului de programare JAVA 19
Tabel 4.1 Diferențe între elementele de tip Class și Enum 45
Tabel 4.2 Logica Fuzzy bazată pe încărcătura liftului 50
Tabel 4.3 Logica Fuzzy bazată pe numărul de opriri al lifturilor 51
Tabel 4.4 Explicația tranzițiilor și locațiilor din modelul componentei de primire și procesare a cererilor 53
Tabel 4.5 Explicația tranzițiilor și locațiilor din modelul componentei de calculare a următoarei opriri și mișcare a liftului 55
ANEXA
Algoritmul de alegere a unui lift
private boolean ChooseElevator(int aFlRq, Dir aDirRq) {
if ((lCtrl1.lEmergency || lCtrl1.IsManual()) && (lCtrl2.lEmergency || lCtrl2.IsManual())){
System.out.println("Ambele lifturi sunt in modul ALARMA si/sau MANUAl, cererea va fi ignorata");
return false;
}
String mod = "ERROR";
if ((lCtrl1.lEmergency && lCtrl1.IsManual()) || (lCtrl2.lEmergency && lCtrl2.IsManual())) {
mod = "ALARMA si MANUAL";
} else if (lCtrl1.lEmergency || lCtrl2.lEmergency) {
mod = "ALARMA";
} else if (lCtrl1.IsManual() || lCtrl2.IsManual()) {
mod = "MANUAL";
}
boolean lRetStatus = false;
Controller lSameFloor = GetIfAtFloor(aFlRq);
if (lSameFloor != null) { //ok
if (!lSameFloor.isFull()) {
System.out.println("Liftul este la acelasi etaj: se cere deschiderea usilor!");
lRetStatus = lSameFloor.GoToFloor(aFlRq, aDirRq);
}
} else if (lCtrl1.lEmergency || lCtrl1.IsManual()) { //ok
System.out.println("Liftul 1 este in modul " + mod + ", se alege Liftul 2.");
lRetStatus = lCtrl2.GoToFloor(aFlRq, aDirRq);
} else if (lCtrl2.lEmergency || lCtrl2.IsManual()) { //ok
System.out.println("Liftul 2 este in modul " + mod + ", se alege Liftul 1.");
lRetStatus = lCtrl1.GoToFloor(aFlRq, aDirRq);
} else if (lCtrl1.lDir == Dir.na && lCtrl2.lDir == Dir.na) { //ok
System.out.println("Ambele lifturi sunt in modul de repaus: se alege cel mai apropiat lift de etajul cererii");
Controller lCon = GetClosest(aFlRq);
if (lCon != null) {
lRetStatus = lCon.GoToFloor(aFlRq, aDirRq);
}
} else if (lCtrl1.lDir == Dir.na && lCtrl2.lDir == Dir.down) { //ok
System.out.println("Liftul 1 este in repaus, Liftul 2 se misca pe directia jos: se executa algoritmul de alegere al liftului…");
if (aDirRq == Dir.up) {
lRetStatus = lCtrl1.GoToFloor(aFlRq, aDirRq);
} else {
if (!lCtrl2.lStopAtFloor[aFlRq].HasDir(Dir.up) && lCtrl2.lFloor > aFlRq && DoFuzzyOnLoad(lCtrl2, aFlRq)) {
lRetStatus = lCtrl2.GoToFloor(aFlRq, aDirRq);
} else {
lRetStatus = lCtrl1.GoToFloor(aFlRq, aDirRq);
}
}
} else if (lCtrl1.lDir == Dir.na && lCtrl2.lDir == Dir.up) { //ok
System.out.println("Liftul 1 este in repaus, Liftul 2 se misca pe directia sus: se executa algoritmul de alegere al liftului…");
if (aDirRq == Dir.down) {
lRetStatus = lCtrl1.GoToFloor(aFlRq, aDirRq);
} else {
if (!lCtrl2.lStopAtFloor[aFlRq].HasDir(Dir.down) && lCtrl2.lFloor < aFlRq && DoFuzzyOnLoad(lCtrl2, aFlRq)) {
lRetStatus = lCtrl2.GoToFloor(aFlRq, aDirRq);
} else {
lRetStatus = lCtrl1.GoToFloor(aFlRq, aDirRq);
}
}
} else if (lCtrl1.lDir == Dir.down && lCtrl2.lDir == Dir.na) { //ok
System.out.println("Liftul 1 se misca pe directia jos, Liftul 2 este in repaus: se executa algoritmul de alegere al liftului…");
if (aDirRq == Dir.up) {
lRetStatus = lCtrl2.GoToFloor(aFlRq, aDirRq); // ctrl2
} else {
if (!lCtrl1.lStopAtFloor[aFlRq].HasDir(Dir.up) && lCtrl1.lFloor > aFlRq && DoFuzzyOnLoad(lCtrl1, aFlRq)) {
lRetStatus = lCtrl1.GoToFloor(aFlRq, aDirRq);
} else {
lRetStatus = lCtrl2.GoToFloor(aFlRq, aDirRq);
}
}
} else if (lCtrl1.lDir == Dir.up && lCtrl2.lDir == Dir.na) { //ok
System.out.println("Liftul 1 se misca pe directia sus, Liftul 2 este in repaus: se executa algoritmul de alegere al liftului…");
if (aDirRq == Dir.down) {
lRetStatus = lCtrl2.GoToFloor(aFlRq, aDirRq);// ctrl 2
} else {
if (!lCtrl1.lStopAtFloor[aFlRq].HasDir(Dir.down) && lCtrl1.lFloor < aFlRq && DoFuzzyOnLoad(lCtrl1, aFlRq)) {
lRetStatus = lCtrl1.GoToFloor(aFlRq, aDirRq);
} else {
lRetStatus = lCtrl2.GoToFloor(aFlRq, aDirRq);
}
}
} else if (lCtrl1.lDir == Dir.down && lCtrl2.lDir == Dir.down) {
System.out.println("Ambele lifturi se misca pe directia jos: se executa algoritmul de alegere al liftului…");
lRetStatus = GetForDownDir(aFlRq, aDirRq).GoToFloor(aFlRq, aDirRq);
} else if (lCtrl1.lDir == Dir.up && lCtrl2.lDir == Dir.up) {
System.out.println("Ambele lifturi se misca pe directia sus: se executa algoritmul de alegere al liftului…");
lRetStatus = GetForUpDir(aFlRq, aDirRq).GoToFloor(aFlRq, aDirRq);
} else if ((lCtrl1.lDir == Dir.up && lCtrl2.lDir == Dir.down) || (lCtrl1.lDir == Dir.down && lCtrl2.lDir == Dir.up)) {
lRetStatus = GetForBoth(aFlRq, aDirRq).GoToFloor(aFlRq, aDirRq);
}
return lRetStatus;
}
Mișcarea liftului și alegerea următoarei opriri
public void run() {
try {
synchronized (this) {
this.wait();
}
while (lRunning) {
lIdle = false;
synchronized (this) {
if (GetNextStop(false, true)) {
lCtrl.SetFloor(0);
lElev.ShowNextStop(String.valueOf(lNextStop));
while (lWork && lCtrl.lFloor > -1 && lCtrl.lFloor < 6 && (lCtrl.lFloor != lNextStop)){// || lCtrl.lInsideReqs[lCtrl.lFloor])) {
for (; i < Main.floorDist; i++) {
lElev.y1 -= lDirHelper;
Thread.sleep(lSpeed);
}
i = 0;
synchronized (lQueueSync) {
lCtrl.SetFloor(lDirHelper);
}
}
lNextStop = -1;
lCtrl.lCanOpenDoors = true;
lCtrl.WorkerStopped();
} else {
lCtrl.lDir = Dir.na;
lCtrl.lCanOpenDoors = true;
lCtrl.SetFloor(0);
lElev.ShowNextStop("-");
lIdle = true;
}
this.wait();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private boolean GetNextStop(boolean aSetQueue, boolean aRecursive) {
synchronized (lSync) {
int j, stopUp = -1, stopDown = -1, lStop = -1, iStop;
boolean up = false, down = false, isFull = lCtrl.isFull();
if (lCtrl.lDir == Dir.na && lResumeDir != Dir.na) {
lCtrl.lDir = lResumeDir;
lDirHelper = (lCtrl.lDir == Dir.up) ? 1 : -1;
lResumeDir = Dir.na;
}
if (!isFull && !lElev.lManual) {
for (j = lCtrl.lFloor + 1; j < Main.totalFloors; j++) {
if (lCtrl.lStopAtFloor[j].HasDir(lCtrl.lDir)) {
up = true;
stopUp = j;
break;
} else if (lCtrl.lStopAtFloor[j] != Dir.na) {
up = true;
stopUp = j;
}
}
for (j = lCtrl.lFloor – 1; j > -1; j–) {
if (lCtrl.lStopAtFloor[j].HasDir(lCtrl.lDir)) {
down = true;
stopDown = j;
break;
} else if (lCtrl.lStopAtFloor[j] != Dir.na) {
down = true;
stopDown = j;
}
}
} else {
lResumeDir = lCtrl.lDir;
if (isFull) {
System.out.println("Liftul " + (lElev.ID + 1) + " este plin: se ignora cererile de la panoul extern!");
} else {
System.out.println("Liftul " + (lElev.ID + 1) + " este in modul MANUAL: se ignora cererile de la panoul extern!");
}
}
iStop = GetInsideStop();
if (iStop > -1 && !up && !down) {
if (isFull || lElev.lManual) {
System.out.println("Urmatoarea oprire a liftului va fi aleasa din cererile panoului intern");
}
lStop = iStop;
} else if (up && lCtrl.lDir == Dir.up) {
if (iStop > -1 && (iStop < stopUp || (iStop > stopUp && !lCtrl.lStopAtFloor[stopUp].HasDir(lCtrl.lDir)))) {
lStop = iStop;
} else {
lStop = stopUp;
}
} else if (down && lCtrl.lDir == Dir.down) {
if(iStop > stopDown || (iStop > -1 && iStop < stopDown && !lCtrl.lStopAtFloor[stopDown].HasDir(lCtrl.lDir))) {
lStop = iStop;
} else {
lStop = stopDown;
}
} else if (!aSetQueue && lCtrl.lDir == Dir.up && !up) {
if (iStop > lCtrl.lFloor){
lStop = iStop;
} else if (!isFull) {
lDirHelper = -1;
lCtrl.lDir = Dir.down;
if (aRecursive) { return GetNextStop(false, false); }
}
} else if (!aSetQueue && lCtrl.lDir == Dir.down && !down) {
if (iStop > -1 && iStop < lCtrl.lFloor){
lStop = iStop;
} else if (!isFull) {
lDirHelper = 1;
lCtrl.lDir = Dir.up;
if (aRecursive) { return GetNextStop(false, false); }
}
}
if(lStop != -1 && lStop != lNextStop) {
if (aSetQueue){
synchronized (lQueueSync) {
if((lCtrl.lDir == Dir.up && lStop >= lCtrl.lFloor) || (lCtrl.lDir == Dir.down && lStop <= lCtrl.lFloor)) {
lNextStop = lStop;
lElev.ShowNextStop(String.valueOf(lNextStop));
}
}
} else {
lNextStop = lStop;
}
System.out.println("Urmatoarea oprire a liftului " + (lElev.ID + 1) + ": " + lStop + "\n");
}
lStopB = lStop != -1;
return lStopB;
}
}
private int GetInsideStop() {
int j, jj = -1;
if (lCtrl.lDir == Dir.down) {
for (j = lCtrl.lFloor – 1; j > -1; j–) {
if (lCtrl.lInsideReqs[j]) {
jj = j;
break;
}
}
} else if (lCtrl.lDir == Dir.up) {
for (j = lCtrl.lFloor + 1; j < Main.totalFloors; j++) {
if (lCtrl.lInsideReqs[j]) {
jj = j;
break;
}
}
}
return jj;
}
protected void Start(int aFloor) {
synchronized (lSync) {
if (lIdle) {
synchronized (this) { }
if (aFloor > -1) {
lCtrl.lDir = (lCtrl.lFloor < aFloor) ? Dir.up : Dir.down;
lDirHelper = (lCtrl.lDir == Dir.up) ? 1 : -1;
}
if (!lElev.lDoorsOpen) {
this.RunNoty();
}
} else if (!lStopB) {
synchronized (this) { }
if (lIdle) {
if (aFloor > -1) {
lCtrl.lDir = (lCtrl.lFloor < aFloor) ? Dir.up : Dir.down;
lDirHelper = (lCtrl.lDir == Dir.up) ? 1 : -1;
}
if (!lElev.lDoorsOpen) {
this.RunNoty();
}
}
} else {
GetNextStop(true, false);
}
}
}
Bibliografie
[1] http://www.sfgate.com/homeandgarden/article/Louis-XV-elevator-king-2658083.php
[2] http://www.britannica.com/EBchecked/topic/434691/Elisha-Graves-Otis
[3] http://www.mitsubishielectric.com/elevator/overview/elevators/history.html
[4] http://elevation.wikia.com/wiki/Elevator_control_system
[5] Irmak, E. ; Colak, I. ; Kaplan, O. ; Kose, A., ”Development of a real time monitoring and control system for PLC based elevator”, Power Electronics and Applications (EPE 2011) Proceedings of the 2011-14th European Conference on Publication, 2011
[6] Xiaoling Yang ; Qunxiong Zhu ; Hong Xu, ”Design and Practice of an Elevator Control System Based on PLC”, Power Electronics and Intelligent Transportation System PEITS '08 Workshop, 2008
[7] Atmel’s Self-Programming Flash Microcontrollers, http://www.atmel.com/Images/doc2464.pdf
[8] Becker, A., ”Microcontroller based elevator controlling system”, Electronics Technology 30th International Spring Seminar, 2007
[9] Huseinbegovic, S. ; Kreso, S. ; Tanovic, O., ”Development of a distributed elevator control system based on the microcontroller PIC 18F458”, Computational Technologies in Electrical and Electronics Engineering (SIBIRCON) 2010 IEEE Region 8 International Conference, 2010
[10] Zhu Deven ; Wu Chengdong, ”Fuzzy control of group elevators”, TENCON '93. Proceedings. Computer, Communication, Control and Power Engineering IEEE Region 10 Conference, 1993
[11] Gu deying ; Yan dongmei, ”Study on Fuzzy Algorithm of Elevator Group Control System”, Challenges in Environmental Science and Computer Engineering (CESCE) IEEE International Conference, 2010
[12] Jing Wang ; Xiaomu Mu ; Jun Xu ; Shuning Wang, ”Design and optimization of dispatching rules for elevator group control aiming at energy saving”, Information Science and Technology (ICIST) IEEE International Conference, 2012
[13] Zhu Dewen ; Jiang Li ; Zhou Yuwen ; Shan Guanghui ; He Kai, ”Modern elevator group supervisory control systems and neural networks technique”, Intelligent Processing Systems ICIPS '97. IEEE International Conference, 1997
[14] http://www.otisworldwide.com/pdf/AboutElevators.pdf
[15] Thomas H. Cormen, ”Algorithms Unlocked”, The MIT Press, 2013
[16] Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, Clifford Stein, ”Introduction to Algorithms, Third Edition”, The MIT Press, 2009
[17] Tyson Macaulay, Bryan Singer, ”Cybersecurity for Industrial Control Systems: SCADA, DCS, PLC, HMI, and SIS”, Auerbach Publications, 2012
[18] Robert Radvanovsky and Jacob Brodsky, ”Handbook of SCADA/Control Systems Security”, Auerbach Publications, 2013
[19] Herbert Schildt, ”Java: The Complete Reference”, Oracle Press, 2011
[20] Michael T. Goodrich, Roberto Tamassia, ”Data Structures and Algorithms in Java”, John Wiley & Sons, 2001
[21] Partha Kuchana, ”Software Architecture Design Patterns in Java”, Auerbach Publications, 2004
[22] Wil van der Aalst, Kees van Hee, ”Workflow Management: Models, Methods, and Systems”, The MIT Press, 2002
[23] Tim Weilkiens, ” Systems Engineering with SysML/UML: Modeling, Analysis, Design”, Morgan Kaufmann Publishers, 2007
[24] Kevin Lano (ed), ”UML 2 Semantics and Applications”, John Wiley & Sons, 2009
[25] Bruce Powel Douglass, ”Real-Time UML Workshop for Embedded Systems, Second Edition”, Newnes, 2014
ANEXA
Algoritmul de alegere a unui lift
private boolean ChooseElevator(int aFlRq, Dir aDirRq) {
if ((lCtrl1.lEmergency || lCtrl1.IsManual()) && (lCtrl2.lEmergency || lCtrl2.IsManual())){
System.out.println("Ambele lifturi sunt in modul ALARMA si/sau MANUAl, cererea va fi ignorata");
return false;
}
String mod = "ERROR";
if ((lCtrl1.lEmergency && lCtrl1.IsManual()) || (lCtrl2.lEmergency && lCtrl2.IsManual())) {
mod = "ALARMA si MANUAL";
} else if (lCtrl1.lEmergency || lCtrl2.lEmergency) {
mod = "ALARMA";
} else if (lCtrl1.IsManual() || lCtrl2.IsManual()) {
mod = "MANUAL";
}
boolean lRetStatus = false;
Controller lSameFloor = GetIfAtFloor(aFlRq);
if (lSameFloor != null) { //ok
if (!lSameFloor.isFull()) {
System.out.println("Liftul este la acelasi etaj: se cere deschiderea usilor!");
lRetStatus = lSameFloor.GoToFloor(aFlRq, aDirRq);
}
} else if (lCtrl1.lEmergency || lCtrl1.IsManual()) { //ok
System.out.println("Liftul 1 este in modul " + mod + ", se alege Liftul 2.");
lRetStatus = lCtrl2.GoToFloor(aFlRq, aDirRq);
} else if (lCtrl2.lEmergency || lCtrl2.IsManual()) { //ok
System.out.println("Liftul 2 este in modul " + mod + ", se alege Liftul 1.");
lRetStatus = lCtrl1.GoToFloor(aFlRq, aDirRq);
} else if (lCtrl1.lDir == Dir.na && lCtrl2.lDir == Dir.na) { //ok
System.out.println("Ambele lifturi sunt in modul de repaus: se alege cel mai apropiat lift de etajul cererii");
Controller lCon = GetClosest(aFlRq);
if (lCon != null) {
lRetStatus = lCon.GoToFloor(aFlRq, aDirRq);
}
} else if (lCtrl1.lDir == Dir.na && lCtrl2.lDir == Dir.down) { //ok
System.out.println("Liftul 1 este in repaus, Liftul 2 se misca pe directia jos: se executa algoritmul de alegere al liftului…");
if (aDirRq == Dir.up) {
lRetStatus = lCtrl1.GoToFloor(aFlRq, aDirRq);
} else {
if (!lCtrl2.lStopAtFloor[aFlRq].HasDir(Dir.up) && lCtrl2.lFloor > aFlRq && DoFuzzyOnLoad(lCtrl2, aFlRq)) {
lRetStatus = lCtrl2.GoToFloor(aFlRq, aDirRq);
} else {
lRetStatus = lCtrl1.GoToFloor(aFlRq, aDirRq);
}
}
} else if (lCtrl1.lDir == Dir.na && lCtrl2.lDir == Dir.up) { //ok
System.out.println("Liftul 1 este in repaus, Liftul 2 se misca pe directia sus: se executa algoritmul de alegere al liftului…");
if (aDirRq == Dir.down) {
lRetStatus = lCtrl1.GoToFloor(aFlRq, aDirRq);
} else {
if (!lCtrl2.lStopAtFloor[aFlRq].HasDir(Dir.down) && lCtrl2.lFloor < aFlRq && DoFuzzyOnLoad(lCtrl2, aFlRq)) {
lRetStatus = lCtrl2.GoToFloor(aFlRq, aDirRq);
} else {
lRetStatus = lCtrl1.GoToFloor(aFlRq, aDirRq);
}
}
} else if (lCtrl1.lDir == Dir.down && lCtrl2.lDir == Dir.na) { //ok
System.out.println("Liftul 1 se misca pe directia jos, Liftul 2 este in repaus: se executa algoritmul de alegere al liftului…");
if (aDirRq == Dir.up) {
lRetStatus = lCtrl2.GoToFloor(aFlRq, aDirRq); // ctrl2
} else {
if (!lCtrl1.lStopAtFloor[aFlRq].HasDir(Dir.up) && lCtrl1.lFloor > aFlRq && DoFuzzyOnLoad(lCtrl1, aFlRq)) {
lRetStatus = lCtrl1.GoToFloor(aFlRq, aDirRq);
} else {
lRetStatus = lCtrl2.GoToFloor(aFlRq, aDirRq);
}
}
} else if (lCtrl1.lDir == Dir.up && lCtrl2.lDir == Dir.na) { //ok
System.out.println("Liftul 1 se misca pe directia sus, Liftul 2 este in repaus: se executa algoritmul de alegere al liftului…");
if (aDirRq == Dir.down) {
lRetStatus = lCtrl2.GoToFloor(aFlRq, aDirRq);// ctrl 2
} else {
if (!lCtrl1.lStopAtFloor[aFlRq].HasDir(Dir.down) && lCtrl1.lFloor < aFlRq && DoFuzzyOnLoad(lCtrl1, aFlRq)) {
lRetStatus = lCtrl1.GoToFloor(aFlRq, aDirRq);
} else {
lRetStatus = lCtrl2.GoToFloor(aFlRq, aDirRq);
}
}
} else if (lCtrl1.lDir == Dir.down && lCtrl2.lDir == Dir.down) {
System.out.println("Ambele lifturi se misca pe directia jos: se executa algoritmul de alegere al liftului…");
lRetStatus = GetForDownDir(aFlRq, aDirRq).GoToFloor(aFlRq, aDirRq);
} else if (lCtrl1.lDir == Dir.up && lCtrl2.lDir == Dir.up) {
System.out.println("Ambele lifturi se misca pe directia sus: se executa algoritmul de alegere al liftului…");
lRetStatus = GetForUpDir(aFlRq, aDirRq).GoToFloor(aFlRq, aDirRq);
} else if ((lCtrl1.lDir == Dir.up && lCtrl2.lDir == Dir.down) || (lCtrl1.lDir == Dir.down && lCtrl2.lDir == Dir.up)) {
lRetStatus = GetForBoth(aFlRq, aDirRq).GoToFloor(aFlRq, aDirRq);
}
return lRetStatus;
}
Mișcarea liftului și alegerea următoarei opriri
public void run() {
try {
synchronized (this) {
this.wait();
}
while (lRunning) {
lIdle = false;
synchronized (this) {
if (GetNextStop(false, true)) {
lCtrl.SetFloor(0);
lElev.ShowNextStop(String.valueOf(lNextStop));
while (lWork && lCtrl.lFloor > -1 && lCtrl.lFloor < 6 && (lCtrl.lFloor != lNextStop)){// || lCtrl.lInsideReqs[lCtrl.lFloor])) {
for (; i < Main.floorDist; i++) {
lElev.y1 -= lDirHelper;
Thread.sleep(lSpeed);
}
i = 0;
synchronized (lQueueSync) {
lCtrl.SetFloor(lDirHelper);
}
}
lNextStop = -1;
lCtrl.lCanOpenDoors = true;
lCtrl.WorkerStopped();
} else {
lCtrl.lDir = Dir.na;
lCtrl.lCanOpenDoors = true;
lCtrl.SetFloor(0);
lElev.ShowNextStop("-");
lIdle = true;
}
this.wait();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private boolean GetNextStop(boolean aSetQueue, boolean aRecursive) {
synchronized (lSync) {
int j, stopUp = -1, stopDown = -1, lStop = -1, iStop;
boolean up = false, down = false, isFull = lCtrl.isFull();
if (lCtrl.lDir == Dir.na && lResumeDir != Dir.na) {
lCtrl.lDir = lResumeDir;
lDirHelper = (lCtrl.lDir == Dir.up) ? 1 : -1;
lResumeDir = Dir.na;
}
if (!isFull && !lElev.lManual) {
for (j = lCtrl.lFloor + 1; j < Main.totalFloors; j++) {
if (lCtrl.lStopAtFloor[j].HasDir(lCtrl.lDir)) {
up = true;
stopUp = j;
break;
} else if (lCtrl.lStopAtFloor[j] != Dir.na) {
up = true;
stopUp = j;
}
}
for (j = lCtrl.lFloor – 1; j > -1; j–) {
if (lCtrl.lStopAtFloor[j].HasDir(lCtrl.lDir)) {
down = true;
stopDown = j;
break;
} else if (lCtrl.lStopAtFloor[j] != Dir.na) {
down = true;
stopDown = j;
}
}
} else {
lResumeDir = lCtrl.lDir;
if (isFull) {
System.out.println("Liftul " + (lElev.ID + 1) + " este plin: se ignora cererile de la panoul extern!");
} else {
System.out.println("Liftul " + (lElev.ID + 1) + " este in modul MANUAL: se ignora cererile de la panoul extern!");
}
}
iStop = GetInsideStop();
if (iStop > -1 && !up && !down) {
if (isFull || lElev.lManual) {
System.out.println("Urmatoarea oprire a liftului va fi aleasa din cererile panoului intern");
}
lStop = iStop;
} else if (up && lCtrl.lDir == Dir.up) {
if (iStop > -1 && (iStop < stopUp || (iStop > stopUp && !lCtrl.lStopAtFloor[stopUp].HasDir(lCtrl.lDir)))) {
lStop = iStop;
} else {
lStop = stopUp;
}
} else if (down && lCtrl.lDir == Dir.down) {
if(iStop > stopDown || (iStop > -1 && iStop < stopDown && !lCtrl.lStopAtFloor[stopDown].HasDir(lCtrl.lDir))) {
lStop = iStop;
} else {
lStop = stopDown;
}
} else if (!aSetQueue && lCtrl.lDir == Dir.up && !up) {
if (iStop > lCtrl.lFloor){
lStop = iStop;
} else if (!isFull) {
lDirHelper = -1;
lCtrl.lDir = Dir.down;
if (aRecursive) { return GetNextStop(false, false); }
}
} else if (!aSetQueue && lCtrl.lDir == Dir.down && !down) {
if (iStop > -1 && iStop < lCtrl.lFloor){
lStop = iStop;
} else if (!isFull) {
lDirHelper = 1;
lCtrl.lDir = Dir.up;
if (aRecursive) { return GetNextStop(false, false); }
}
}
if(lStop != -1 && lStop != lNextStop) {
if (aSetQueue){
synchronized (lQueueSync) {
if((lCtrl.lDir == Dir.up && lStop >= lCtrl.lFloor) || (lCtrl.lDir == Dir.down && lStop <= lCtrl.lFloor)) {
lNextStop = lStop;
lElev.ShowNextStop(String.valueOf(lNextStop));
}
}
} else {
lNextStop = lStop;
}
System.out.println("Urmatoarea oprire a liftului " + (lElev.ID + 1) + ": " + lStop + "\n");
}
lStopB = lStop != -1;
return lStopB;
}
}
private int GetInsideStop() {
int j, jj = -1;
if (lCtrl.lDir == Dir.down) {
for (j = lCtrl.lFloor – 1; j > -1; j–) {
if (lCtrl.lInsideReqs[j]) {
jj = j;
break;
}
}
} else if (lCtrl.lDir == Dir.up) {
for (j = lCtrl.lFloor + 1; j < Main.totalFloors; j++) {
if (lCtrl.lInsideReqs[j]) {
jj = j;
break;
}
}
}
return jj;
}
protected void Start(int aFloor) {
synchronized (lSync) {
if (lIdle) {
synchronized (this) { }
if (aFloor > -1) {
lCtrl.lDir = (lCtrl.lFloor < aFloor) ? Dir.up : Dir.down;
lDirHelper = (lCtrl.lDir == Dir.up) ? 1 : -1;
}
if (!lElev.lDoorsOpen) {
this.RunNoty();
}
} else if (!lStopB) {
synchronized (this) { }
if (lIdle) {
if (aFloor > -1) {
lCtrl.lDir = (lCtrl.lFloor < aFloor) ? Dir.up : Dir.down;
lDirHelper = (lCtrl.lDir == Dir.up) ? 1 : -1;
}
if (!lElev.lDoorsOpen) {
this.RunNoty();
}
}
} else {
GetNextStop(true, false);
}
}
}
Copyright Notice
© Licențiada.org respectă drepturile de proprietate intelectuală și așteaptă ca toți utilizatorii să facă același lucru. Dacă consideri că un conținut de pe site încalcă drepturile tale de autor, te rugăm să trimiți o notificare DMCA.
Acest articol: Functionalitatea Si Controlul Optim al Lifturilor (ID: 162515)
Dacă considerați că acest conținut vă încalcă drepturile de autor, vă rugăm să depuneți o cerere pe pagina noastră Copyright Takedown.
