Advertisement
Guest User

Untitled

a guest
Sep 24th, 2017
71
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 21.40 KB | None | 0 0
  1. #include "termIn.h"
  2.  
  3. #define DIN_BUTTON 0
  4. #define DIN_TERMINALLOOP 1
  5. #define DIN_GATELOOP 2
  6. #define DOUT_RELAY4 3
  7. #define DOUT_RELAY1 0
  8.  
  9. #ifdef __cplusplus
  10. extern "C"
  11. {
  12. #endif
  13.  
  14. #include <pixtend.h>
  15.  
  16. #ifdef __cplusplus
  17. }
  18. #endif
  19.  
  20. termIn::termIn()
  21. {
  22. //ctor
  23. //TODO set by args
  24. setDisplayButtonPress=false;
  25. buttonWasPressed=false;
  26. displayOff=false;
  27. }
  28.  
  29. termIn::~termIn()
  30. {
  31. //dtor
  32. }
  33.  
  34. void termIn::cardPresent(mutekDispenser* d){
  35. try
  36. {
  37. d->statusCheck();
  38. egateTimer::esleep(10000);
  39. }
  40. catch(std::exception &e)
  41. {
  42. std::cout<<"Exception in cardPresent:"<<e.what()<< std::endl;
  43. }
  44. }
  45.  
  46. void termIn::maintainButtonPressed(){
  47. int dinVal=0;
  48. int doutVal=0;
  49. int bitPosition=0;
  50.  
  51. Spi_Setup(0);
  52.  
  53. while(1==1){
  54. dinVal= Spi_Get_Din();
  55. // if pressed button
  56. if ((dinVal & (1 << bitPosition)) >> bitPosition == 1)
  57. {
  58. doutVal = Spi_Get_Relays();
  59. if ((doutVal & (1 << 3))>>3 ==0)
  60. Spi_Set_Relays(doutVal+8);
  61. }
  62. }
  63. }
  64.  
  65. void termIn::runTerminalLoop(){
  66.  
  67. int dinVal=0;
  68.  
  69. //beforeRfidReadTime = std::chrono::high_resolution_clock::now();
  70.  
  71. // std::string rezTest=dal.hasReaderLogSince(1,100000);
  72.  
  73. //std::thread thread1;
  74.  
  75. //while(1==1)
  76. //{
  77. try
  78. {
  79. dinVal = Spi_Get_Din();
  80.  
  81. // Terminal Loop Active (loop at gate)
  82. if ((dinVal & (1<< DIN_TERMINALLOOP))>> DIN_TERMINALLOOP == 1)
  83. {
  84. displayOff=false;
  85. if (!dispenser.stateAcceptCardIn)
  86. {
  87. beforeRfidReadTime = std::chrono::high_resolution_clock::now();
  88. dispenser.cardInEnable();
  89. }
  90.  
  91. //TODO: Display command press button
  92. if (setDisplayButtonPress==false)
  93. {
  94. display.displayLines("PRESS THE BUTTON ","PRITISNITE DUGME", 250, terminalId);
  95. setDisplayButtonPress=true;
  96. }
  97.  
  98. //create new thread and check for card present for prepaid
  99. if (atTerminalLoopCounter%100==0)
  100. dispenser.statusCheck(); // for now don't use threading
  101. //thread1=std::thread(termIn::cardPresent, &dispenser);
  102.  
  103. if (dispenser.sensorRead && dispenser.deconstructStatusOk)
  104. {
  105. //TOOD: entrance for prepaid
  106. display.displayLines("PREPAID CARD CHECK","PROVJERA KARTICE", 250, terminalId);
  107.  
  108. issueNewPrepaidTicket();
  109.  
  110. setDisplayButtonPress=false;
  111. buttonWasPressed=false;
  112. beforeRfidReadTime = std::chrono::high_resolution_clock::now();
  113. dispenser.statusCheck();
  114. //atTerminalLoopCounter=0;
  115. }
  116.  
  117. //Button is pressed
  118. if (((dinVal & (1<< DIN_BUTTON))>> DIN_BUTTON == 1) || buttonWasPressed)
  119. {
  120. // Finish t1 thread checking already accepted Prepaid potential
  121. if (thread1.joinable())
  122. thread1.join();
  123.  
  124. if (!dispenser.sensorRead)
  125. {
  126. issueNewTicket();
  127.  
  128. setDisplayButtonPress=false;
  129. buttonWasPressed=false;
  130. dispenser.deconstructStatusOk = false;
  131. //atTerminalLoopCounter=0;
  132. dinVal=0;
  133. }
  134. }
  135.  
  136. if (thread1.joinable())
  137. thread1.join();
  138.  
  139. if (atTerminalLoopCounter>1000000)
  140. atTerminalLoopCounter=0;
  141. else
  142. atTerminalLoopCounter++;
  143.  
  144. displayOff=false;
  145. }
  146. else
  147. {
  148. if (dispenser.stateAcceptCardIn)
  149. {
  150. dispenser.statusCheck();
  151. if (dispenser.stateAcceptCardIn)
  152. dispenser.cardInDisable();
  153. }
  154.  
  155. if (!displayOff)
  156. {
  157. display.displayLines("","",0,terminalId);
  158. egateTimer::esleep(100000);
  159. displayOff=true;
  160. }
  161.  
  162. if (setDisplayButtonPress)
  163. setDisplayButtonPress=false;
  164. }
  165.  
  166. egateTimer::esleep(10000);
  167. }
  168. catch(std::exception &ex)
  169. {
  170. std::cout<<"Main loop error: ";
  171. std::cout<<ex.what()<<std::endl;
  172. }
  173.  
  174. //}// while
  175.  
  176. //t1.join();
  177. }
  178.  
  179. void termIn::issueNewPrepaidTicket()
  180. {
  181. afterRfidReadTime = std::chrono::high_resolution_clock::now();
  182. std::string rfidReadout;
  183.  
  184. rfidReadout = getPrepaidRfidReadout();
  185.  
  186. if (isNumeric(rfidReadout))
  187. {
  188. if (dal.createTicket(rfidReadout, terminalId))
  189. {
  190. display.displayLines("PREPAID VALID","KARTICA VALIDNA",250, terminalId);
  191. dispenseTicketWithEntrence();
  192. std::cout<<"Created new Ticket "<<std::endl;
  193.  
  194. return;
  195. }
  196. }
  197.  
  198. std::cout<<"Error on prepaidread ... dispanse back ..."<<std::endl;
  199. std::cout<<"Ticket was not created"<<std::endl;
  200. // TODO ? Probably read ID was not a ticket
  201. display.displayLines("INVALID CARD","NEISPRAVNA KARTICA",250, terminalId);
  202.  
  203. dispenseTicketWithNoEntrence();
  204. }
  205.  
  206. void termIn::issueNewTicket(){
  207. beforeRfidReadTime = std::chrono::high_resolution_clock::now();
  208. afterRfidReadTime = std::chrono::high_resolution_clock::now();
  209. std::string rfidReadout="";
  210.  
  211. display.displayLines("CREATING NEW TICKET", "KREIRAM NOVI TIKET", 250, terminalId);
  212. for (int i=0; i<50; i++)
  213. {
  214. dispenser.statusCheck();
  215. if (dispenser.deconstructStatusOk)
  216. i=50;
  217. else
  218. {
  219. std::cout<<"deconstructStatusOk was false, looping for status ..."<<std::endl<<std::flush;
  220. if (i>0)
  221. egateTimer::esleep(200000);
  222. else
  223. egateTimer::esleep(100000);
  224. }
  225. }
  226.  
  227. if (dispenser.statusDispenseReady)
  228. {
  229. // save time before dispenser sends card to read position
  230. beforeRfidReadTime = std::chrono::high_resolution_clock::now();
  231. dispenser.dispenseToRead(true);
  232.  
  233. for (int i=0; i<10; i++)
  234. {
  235. dispenser.statusCheck();
  236. if (dispenser.deconstructStatusOk && dispenser.sensorRead)
  237. i=10;
  238. else
  239. {
  240. if(!dispenser.deconstructStatusOk)
  241. std::cout<<"DispenseToRead unseccessfull, running once more"<<std::endl<<std::flush;
  242.  
  243. rfidReadout = getRfidReadout();
  244. }
  245. if (rfidReadout!="")
  246. {
  247. std::cout<<"Ticket read fetched => skipping read sensor status..."<<std::endl<<std::flush;
  248. i=10;
  249. }else
  250. egateTimer::esleep(100000);
  251. }
  252.  
  253. if(rfidReadout=="")
  254. rfidReadout = getRfidReadout();
  255.  
  256. if (dal.createTicket(rfidReadout, terminalId))
  257. {
  258. std::cout<<"Created new Ticket "<<std::endl<<std::flush;
  259. dispenseTicketWithEntrence();
  260. }
  261. else
  262. {
  263. std::cout<<"Ticket was not created"<<std::endl;
  264. dal.displayEventLog("Neuspio pokušaj kreiranja tiketa na terminalu "+std::to_string(terminalId)
  265. ,3
  266. ,"Ticket was not created. Dal.createTicket call returned false.");
  267. dispenser.collect();
  268. display.displayLines("Card error","Greska na kartici",250, terminalId);
  269. egateTimer::esleep(1000000);
  270. display.displayLines("Try again","Probajte ponovno",250, terminalId);
  271. egateTimer::esleep(1000000);
  272. dispenser.statusCheck();
  273. }
  274. }
  275. else
  276. {
  277. //TODO: Throw error and log ...
  278. std::cout<<"IssueNewTicket StatusDispense error, statusDispenseReady was false."<<std::flush;
  279. dal.displayEventLog("Neuspio pokušaj kreiranja tiketa na terminalu "+std::to_string(terminalId)
  280. ,3
  281. ,"Ticket was not created. Dispenser.statusDispenseReady call returned false.");
  282. display.displayLines("Card error","Greska na kartici",250, terminalId);
  283. dispenser.reset();
  284. egateTimer::esleep(2000000);
  285. dispenser.statusCheck();
  286. }
  287. }
  288.  
  289.  
  290. std::string termIn::getPrepaidRfidReadout()
  291. {
  292. std::chrono::high_resolution_clock::time_point waitPoint;
  293.  
  294. waitPoint = std::chrono::high_resolution_clock::now();
  295. afterRfidReadTime = std::chrono::high_resolution_clock::now();
  296.  
  297. std::chrono::duration<double> passed = std::chrono::duration_cast<std::chrono::duration<double>>(afterRfidReadTime - beforeRfidReadTime);
  298. std::chrono::duration<double> passedWait = std::chrono::duration_cast<std::chrono::duration<double>>(afterRfidReadTime - waitPoint);
  299. std::string hasRfidRead;
  300. int passedCount=0;
  301. int passedWaitCount=0;
  302. //get rfid readout
  303. try
  304. {
  305. while (hasRfidRead=="" && passedWaitCount < 3000) // TODO: Or time passed is over a minute throw error
  306. {
  307. if (hasRfidRead=="")
  308. {
  309. egateTimer::esleep(200000);
  310. afterRfidReadTime = std::chrono::high_resolution_clock::now();
  311. passed = std::chrono::duration_cast<std::chrono::duration<double>>(afterRfidReadTime - beforeRfidReadTime);
  312. passedWait = std::chrono::duration_cast<std::chrono::duration<double>>(afterRfidReadTime - waitPoint);
  313.  
  314. passedCount=(int)(passed.count()*1000);
  315. passedWaitCount=(int)(passedWait.count()*1000);
  316.  
  317.  
  318. hasRfidRead = dal.hasPrepaidReaderLogSince(terminalId, passedCount);
  319. }
  320. }
  321. }
  322. catch(std::exception &e)
  323. {
  324. //TODO: Report error
  325. std::cout<<e.what()<<std::endl;
  326. hasRfidRead="Error:Cannot read card Neispravna kartica ";
  327. }
  328.  
  329. return hasRfidRead;
  330. }
  331.  
  332. std::string termIn::getRfidReadout()
  333. {
  334. std::chrono::high_resolution_clock::time_point waitPoint;
  335.  
  336. waitPoint = std::chrono::high_resolution_clock::now();
  337.  
  338. std::chrono::duration<double> passed = std::chrono::duration_cast<std::chrono::duration<double>>(afterRfidReadTime - beforeRfidReadTime);
  339. std::chrono::duration<double> passedWait = std::chrono::duration_cast<std::chrono::duration<double>>(afterRfidReadTime - waitPoint);
  340. std::string hasRfidRead="";
  341. int passedCount=0;
  342. int passedWaitCount=0;
  343. //get rfid readout
  344. try
  345. {
  346. while (hasRfidRead=="" && passedWaitCount < 3000) // TODO: Or time passed is over a minute throw error
  347. {
  348. if (hasRfidRead=="")
  349. {
  350. egateTimer::esleep(200000);
  351. afterRfidReadTime = std::chrono::high_resolution_clock::now();
  352. passed = std::chrono::duration_cast<std::chrono::duration<double>>(afterRfidReadTime - beforeRfidReadTime);
  353. passedWait = std::chrono::duration_cast<std::chrono::duration<double>>(afterRfidReadTime - waitPoint);
  354.  
  355. passedCount=(int)(passed.count()*1000);
  356. passedWaitCount=(int)(passedWait.count()*1000);
  357.  
  358. hasRfidRead = dal.hasReaderLogSince(terminalId, passedCount);
  359. }
  360. }
  361. }
  362. catch(std::exception &e)
  363. {
  364. std::cout<<e.what()<<std::endl;
  365. }
  366.  
  367. return hasRfidRead;
  368. }
  369.  
  370. void termIn::dispenseTicketWithEntrence(){
  371. bool cardTaken=false;
  372. int counter=0;
  373. int dinVal=0;
  374.  
  375. for(int i=0; i<30; i++)
  376. {
  377. dispenser.statusCheck(); // check for sensors that ticket is in read possition
  378.  
  379. if (dispenser.deconstructStatusOk && dispenser.sensorRead && dispenser.sensorReadFront)
  380. {
  381. i=100;
  382. }
  383. else
  384. {
  385. std::cout<<"False ons sensor read, readFront or deconstructStatus, looping again for new status ..."<<std::flush;
  386. egateTimer::esleep(100000);
  387. }
  388. }
  389.  
  390. if (dispenser.sensorRead && dispenser.sensorReadFront)
  391. {
  392. //TODO read ticket
  393. //TODO create new ticket in db
  394. display.displayLines("PLEASE TAKE TICKET","UZMITE TIKET",250,terminalId);
  395. dispenser.cardInDisable();
  396. dispenser.dispenseToFront();
  397.  
  398. // wait for front sensor
  399. for(int i=0;i<10;i++)
  400. {
  401. egateTimer::esleep(30000); // wait for machine to push ticket to outside/
  402. dispenser.statusCheck(); // check for sensors that ticket is in read possition
  403.  
  404. if (dispenser.sensorFront)
  405. {
  406. i=10;
  407. std::cout<<"SensorFront True -> Take Ticket"<<std::endl<<std::flush;
  408. }
  409. else
  410. {
  411. if (!dispenser.deconstructStatusOk )
  412. {
  413. std::cout<<"deconstructStatusOk was false, looping again for sensorFront ..."<<std::flush;
  414. egateTimer::esleep(70000); // wait for machine to push ticket to outside/
  415. }
  416. }
  417.  
  418. if (!dispenser.sensorFront && dispenser.deconstructStatusOk && dispenser.statusDispenseReady)
  419. {
  420. std::cout<<"deconstructStatusOk and statusDispenseReady, ticket already taken..."<<std::flush;
  421. i = 10;
  422. }
  423. }
  424.  
  425. if(dispenser.sensorFront)
  426. {
  427. counter=0;
  428.  
  429. while(dispenser.sensorFront && counter<200)
  430. {
  431. dinVal = Spi_Get_Din();
  432. egateTimer::esleep(200000); // wait for machine to push ticket to outside
  433. dispenser.statusCheck();
  434. counter++;
  435.  
  436. if ((dinVal & (1<< DIN_TERMINALLOOP))>> DIN_TERMINALLOOP == 0)
  437. counter=200;
  438.  
  439. if (!dispenser.deconstructStatusOk)
  440. {
  441. std::cout<<"deconstructStatusOk was false waitng 100ms..."<<std::flush;
  442. egateTimer::esleep(100000); // wait for machine to push ticket to outside
  443. }
  444. else
  445. std::cout<<"Wating SensorFront false to taken ticket"<<std::endl<<std::flush;
  446. }
  447.  
  448. if (dispenser.sensorFront)
  449. {
  450. display.displayLines("Ticket canceled","Tiket otkazan",250, terminalId);
  451. dispenser.collect();
  452. dal.displayEventLog("Povucena kartica na terminalu "+std::to_string(terminalId),2,"");
  453. dal.cancelLastTicket(terminalId);
  454. return;
  455. }
  456. }
  457.  
  458. std::cout<<"Ticket TAKEN OPEN GATE !!!"<<std::endl<<std::flush;
  459. raiseGateAndWaitForEntrence();
  460. //display.displayLines("Welcome","Dobrodosli",250,terminalId);
  461.  
  462. if (dispenser.sensorCardPreEmpty)
  463. dal.displayEventLog("Nedostaje kartica na terminalu "+std::to_string(terminalId),2,"");
  464. }
  465. else
  466. {
  467. //TODO Handle error
  468. std::cout<<"Error in dispenser sensorRead and sensorReadFront, read unsecussfull"<<std::flush;
  469. display.displayLines("Ticket canceled","Tiket otkazan",250, terminalId);
  470. dal.displayEventLog("Greska izbacivanja kartice na terminalu "+std::to_string(terminalId),3,"");
  471. dispenser.reset();
  472. }
  473. }
  474.  
  475. void termIn::dispenseTicketWithNoEntrence(){
  476. // NO ENTERRENCE !!!
  477. int dinVal=0;
  478. int counter=0;
  479.  
  480. for(int i=0; i<100; i++)
  481. {
  482. dispenser.statusCheck(); // check for sensors that ticket is in read possition
  483.  
  484. if (dispenser.deconstructStatusOk && dispenser.sensorRead && dispenser.sensorReadFront)
  485. {
  486. i=100;
  487. }
  488. else
  489. {
  490. std::cout<<"False on sensor read, readFront or deconstructStatus, looping again for new status ..."<<std::flush;
  491. egateTimer::esleep(100000);
  492. }
  493. }
  494.  
  495. if (dispenser.sensorRead && dispenser.sensorReadFront)
  496. {
  497. //TODO read ticket
  498. //TODO create new ticket in db
  499. dispenser.dispenseToFront();
  500.  
  501. // wait for front sensor
  502. for(int i=0;i<10;i++)
  503. {
  504. egateTimer::esleep(10000); // wait for machine to push ticket to outside/
  505. dispenser.statusCheck(); // check for sensors that ticket is in read possition
  506.  
  507. if (dispenser.sensorFront)
  508. i=10;
  509. else
  510. {
  511. std::cout<<"deconstructStatusOk was false, looping again for sensorFront ..."<<std::flush;
  512. }
  513. }
  514.  
  515. if(dispenser.sensorFront)
  516. {
  517. display.displayLines("Please take card","Uzmite karticu",250,terminalId);
  518. while(dispenser.sensorFront && counter<100)
  519. {
  520. dinVal = Spi_Get_Din();
  521. egateTimer::esleep(200000); // wait for machine to push ticket to outside
  522. dispenser.statusCheck();
  523.  
  524. if ((dinVal & (1<< DIN_TERMINALLOOP))>> DIN_TERMINALLOOP == 0)
  525. counter=200;
  526.  
  527. counter++;
  528. }
  529.  
  530. if (dispenser.sensorFront)
  531. {
  532. display.displayLines("Invalid card taken","Kartica povucena",250, terminalId);
  533. dispenser.collect();
  534. dal.displayEventLog("Povucena neispravna pretplatnicka kartica na terminalu "+std::to_string(terminalId),2,"");
  535. egateTimer::esleep(2000000); // wait for machine to push ticket to outside
  536. }
  537.  
  538. //display.displayLines("","",0,terminalId);
  539. //egateTimer::esleep(100000); // wait for machine to push ticket to outside
  540. //display.displayLines("","",0,terminalId);
  541. }
  542. }
  543. else
  544. {
  545. //TODO Handle error
  546. std::cout<<"Error in dispenser sensorRead and sensorReadFront, read unsecussfull on invalid card"<<std::flush;
  547. display.displayLines("Ticket canceled","Tiket otkazan",250, terminalId);
  548. dal.displayEventLog("Greska izbacivanja nevalidne kartice na terminalu "+std::to_string(terminalId),3,"");
  549. dispenser.reset();
  550. }
  551. }
  552.  
  553. void termIn::raiseGateAndWaitForEntrence()
  554. {
  555. std::chrono::high_resolution_clock::time_point waitPoint;
  556. std::chrono::high_resolution_clock::time_point checkPoint;
  557.  
  558. waitPoint = std::chrono::high_resolution_clock::now();
  559.  
  560.  
  561. int counter=0;
  562. setRelayValue(DOUT_RELAY1, true);
  563.  
  564. display.displayLines("Welcome","Dobrodosli",250,terminalId);
  565.  
  566. checkPoint = std::chrono::high_resolution_clock::now();
  567. std::chrono::duration<double> passed = std::chrono::duration_cast<std::chrono::duration<double>>(checkPoint - waitPoint);
  568.  
  569. if (abs((int)(passed.count()*10)) < 6)
  570. {
  571. int waitInt = 6 - abs((int)(passed.count()*10));
  572. egateTimer::esleep(waitInt * 100000);//egateTimer::esleep(500000);
  573. std::cout<<"Waiting additional decisec: "<<waitInt<<std::endl;
  574. }
  575.  
  576. setRelayValue(DOUT_RELAY1, false);
  577.  
  578. int dinVal = Spi_Get_Din();
  579. bool gateLoopPassed=false;
  580. bool gateLoopActivated=false;
  581. bool displaySet = false;
  582.  
  583. // while no car over gate or 2 minutes passed
  584. while(!gateLoopPassed && counter<1000)
  585. {
  586. egateTimer::esleep(100000);
  587. dinVal = Spi_Get_Din();
  588. gateLoopActivated = gateLoopActivated || (((dinVal & (1<<DIN_GATELOOP))>>DIN_GATELOOP) == 1);
  589.  
  590. if (gateLoopActivated)
  591. {
  592. if (!displaySet){
  593. display.displayLines("","",0,terminalId);
  594. displaySet=true;
  595. std::cout<<"Gate Loop Activated"<<std::endl;
  596. }
  597. gateLoopPassed = (((dinVal &(1<<DIN_GATELOOP))>>DIN_GATELOOP) == 0);
  598. }
  599.  
  600. counter++;
  601. }
  602.  
  603. if (gateLoopPassed)
  604. std::cout<<"Gate Loop passed"<<std::endl;
  605. else
  606. std::cout<<"!!!Gate Loop NOT passed"<<std::endl;
  607. }
  608.  
  609. void termIn::setRelayValue(int relayIndex, bool turnOn)
  610. {
  611. int doutVal = Spi_Get_Relays();
  612. int newVal=0;
  613.  
  614. if(turnOn)
  615. {
  616. if ((doutVal & (1<< relayIndex))>>relayIndex == 0)
  617. {
  618. newVal=doutVal + (1<<relayIndex);
  619. Spi_Set_Relays(newVal);
  620. }
  621. }
  622. else
  623. {
  624. if ((doutVal & (1<< relayIndex))>>relayIndex == 1)
  625. {
  626. newVal=doutVal - (1<<relayIndex);
  627. Spi_Set_Relays(newVal);
  628. }
  629. }
  630. }
  631.  
  632. void termIn::Test485()
  633. {
  634. int readChar;
  635. int fd=serialOpen("/dev/ttyUSB0",9600);
  636.  
  637. egateTimer::esleep(500000);
  638. //serialPutchar(fd, 'C');
  639.  
  640. for (int i=0; i<100; i++)
  641. {
  642. while(serialDataAvail(fd))
  643. {
  644. egateTimer::esleep(30000);
  645.  
  646. readChar=serialGetchar(fd);
  647. if (readChar!=-1)
  648. {
  649. std::cout<<readChar;
  650. }
  651. }
  652. }
  653.  
  654.  
  655. serialClose(fd);
  656. }
  657.  
  658. void termIn::resetDispenser()
  659. {
  660. /* dispenser.collect();
  661.  
  662. dispenser.cardInEnable();
  663.  
  664. dispenser.collect();
  665.  
  666. dispenser.cardInDisable();*/
  667.  
  668. dispenser.reset();
  669. }
  670.  
  671. bool termIn::isNumeric(const std::string& s)
  672. {
  673. std::string::const_iterator it=s.begin();
  674.  
  675. while(it!=s.end() && std::isdigit(*it)) ++it;
  676.  
  677. return !s.empty() && it == s.end();
  678. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement