coffee.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998
  1. /*
  2. * coffee.cpp
  3. *
  4. * Created on: Sep 25, 2017
  5. * Author: sebastian
  6. */
  7. #include <stdio.h>
  8. #include <stdlib.h>
  9. #include <string.h>
  10. #include <errno.h>
  11. #include <stdint.h>
  12. #include <wiringPi.h>
  13. #include <pthread.h>
  14. #include <unistd.h>
  15. #include <iostream>
  16. #include <csignal>
  17. #include <time.h>
  18. #include <ctime>
  19. #include "coffee.h"
  20. #include "hal.h"
  21. #include "logger.h"
  22. #include "timer.h"
  23. #include "database.h"
  24. coffee_status_t state;
  25. coffee_menuPage_t page;
  26. coffee_mode_t mode;
  27. int sigValue;
  28. int brewTime; //Brew time in ms
  29. int menuTimeout;
  30. timer brewTimer(&brewTimeHandler);
  31. timer menuTimer(&menuTimeHandler);
  32. uint64_t totalHeatingTime; //local copies of the corresponding database entries
  33. uint16_t brewCounter;
  34. bool initalHeating;
  35. bool descaling; //flag to indicate descaling and cleaning
  36. uint16_t descBrewcount;
  37. time_t descRawTimestamp;
  38. const char* PageName[] = { "SoftOff", "Kill", "Stats", "Stats2", "Descaling", "Temp", "Clean", "Demo",
  39. "Exit" };
  40. const char* StateName[] = { "OFF", "HEATING", "INITHEAT", "IDLE", "BREW",
  41. "BREWMAN", "CLEAN", "ERROR", "WAITOFF" };
  42. /**
  43. * Thread for the finite state machine
  44. * It represents the current state of the machine and handles signals coming from
  45. * the pressure control, buttons, the brew switch and the proximity sensor
  46. * @param threadid the ID of the thread
  47. */
  48. void *coffeeThread(void *threadid) {
  49. logger(V_BASIC, "Initializing coffee thread...\n");
  50. //installing new Signal handler for coffeethread
  51. struct sigaction action;
  52. sigemptyset(&action.sa_mask);
  53. action.sa_flags = SA_SIGINFO;
  54. action.sa_sigaction = coffeeHandler;
  55. sigaction(SIGUSR2, &action, NULL);
  56. brewTimer.setDivider(4);
  57. brewTimer.stop();
  58. brewTime = 0;
  59. menuTimer.setDivider(4);
  60. menuTimer.stop();
  61. menuTimeout = 0;
  62. initalHeating = true;
  63. mode = MODE_STATE; //Unless we enter the menu we start in state mode
  64. page = PAGE_SOFTOFF;
  65. descaling = false;
  66. //read the database values
  67. if (!(totalHeatingTime = sqlGetConf(CFGHeatingTime))) {
  68. logger_error("coffee.cpp: Couldn't read the heating time from the database\n");
  69. //pthread_exit(EXIT_SUCCESS);
  70. exit(EXIT_FAILURE);
  71. }
  72. if (!(brewCounter = sqlGetConf(CFGbrewcounter))) {
  73. logger_error("coffee.cpp: Couldn't read the brew counter from the database\n");
  74. //pthread_exit(EXIT_SUCCESS);
  75. exit(EXIT_FAILURE);
  76. }
  77. if (!(descRawTimestamp = (time_t) sqlGetConf(CFGDescTimestamp))) {
  78. logger_error("coffee.cpp: Couldn't read the descaling time from the database\n");
  79. //pthread_exit(EXIT_SUCCESS);
  80. exit(EXIT_FAILURE);
  81. }
  82. if (!(descBrewcount = sqlGetConf(CFGDescBrewCount))) {
  83. logger_error("coffee.cpp: Couldn't read the descaling brewcount time from the database\n");
  84. //pthread_exit(EXIT_SUCCESS);
  85. exit(EXIT_FAILURE);
  86. }
  87. checkDescaling();
  88. event_subscribe("terminate", &coffeeTerminate);
  89. logger(V_BREW, "Determining inital state\n");
  90. //determine inital state
  91. if (halGetRelaisState(RELAIS_POWER) && halGetRelaisState(RELAIS_HEAT)
  92. && !halGetRelaisState(RELAIS_PUMP)) {
  93. //wait for heat relais to switch
  94. coffeeNap(1,0);
  95. if (halIsHeating()) { //Heating is on
  96. changeState(STATE_INITALHEATING);
  97. } else {
  98. initalHeating = false;
  99. changeState(STATE_IDLE);
  100. }
  101. } else if (halGetRelaisState(RELAIS_PUMP)) {
  102. logger_error("Whoops, why is the pump running...\n");
  103. changeState(STATE_ERROR);
  104. } else {
  105. changeState(STATE_OFF);
  106. }
  107. logger(V_BREW, "Starting Coffee FSM\n");
  108. //begin FSM
  109. while (1) {
  110. /*
  111. * Menue FSM
  112. */
  113. switch (page) {
  114. case PAGE_SOFTOFF: //this page is only available when the machine is on
  115. if (SigValueEmpty() && mode == MODE_MENU)
  116. pause();
  117. switch (getSigValue(MODE_MENU)) {
  118. case SigInt0Rls:
  119. if(halIsHeating()){
  120. changeState(STATE_WAIT_OFF);
  121. }
  122. else {
  123. changeState(STATE_OFF);
  124. }
  125. leaveMenu();
  126. break;
  127. case SigRotCW:
  128. changePage(PAGE_KILL);
  129. break;
  130. case SigRotCCW:
  131. changePage(PAGE_EXIT);
  132. break;
  133. }
  134. break;
  135. case PAGE_KILL: //this page is only available when the machine is on
  136. if (SigValueEmpty() && mode == MODE_MENU)
  137. pause();
  138. switch (getSigValue(MODE_MENU)) {
  139. case SigInt0Rls:
  140. if (halIsHeating()) {
  141. coffeeIncreaseHeatingTime(halgetHeatingTime());
  142. }
  143. changeState(STATE_OFF);
  144. leaveMenu();
  145. break;
  146. case SigRotCW:
  147. if (state == STATE_IDLE || state == STATE_HEATING) {
  148. changePage(PAGE_CLEAN);
  149. } else {
  150. changePage(PAGE_DEMO);
  151. }
  152. break;
  153. case SigRotCCW:
  154. changePage(PAGE_SOFTOFF);
  155. break;
  156. }
  157. break;
  158. case PAGE_CLEAN: //this page is only be available when the machine is hot
  159. if (SigValueEmpty() && mode == MODE_MENU)
  160. pause();
  161. switch (getSigValue(MODE_MENU)) {
  162. case SigInt0Rls:
  163. changeMode(MODE_STATE);
  164. if (!halProxSensorCovered()) {
  165. changeState(STATE_CLEANING);
  166. leaveMenu();
  167. } else {
  168. changeState(STATE_FULLTANK);
  169. leaveMenu();
  170. }
  171. break;
  172. case SigRotCW:
  173. changePage(PAGE_DEMO);
  174. break;
  175. case SigRotCCW:
  176. changePage(PAGE_KILL);
  177. break;
  178. }
  179. break;
  180. case PAGE_DEMO:
  181. if (SigValueEmpty() && mode == MODE_MENU)
  182. pause();
  183. switch (getSigValue(MODE_MENU)) {
  184. case SigRotCW:
  185. changePage(PAGE_TEMP);
  186. break;
  187. case SigRotCCW:
  188. if(state == STATE_IDLE || state == STATE_HEATING){
  189. changePage(PAGE_CLEAN);
  190. } else if (state == STATE_ERROR || state == STATE_INITALHEATING) {
  191. changePage(PAGE_KILL);
  192. } else {
  193. changePage(PAGE_EXIT);
  194. }
  195. break;
  196. }
  197. break;
  198. case PAGE_TEMP:
  199. if (SigValueEmpty() && mode == MODE_MENU)
  200. pause();
  201. switch (getSigValue(MODE_MENU)) {
  202. case SigRotCW:
  203. changePage(PAGE_STATS);
  204. break;
  205. case SigRotCCW:
  206. changePage(PAGE_DEMO);
  207. break;
  208. }
  209. break;
  210. case PAGE_STATS:
  211. if (SigValueEmpty() && mode == MODE_MENU)
  212. pause();
  213. switch (getSigValue(MODE_MENU)) {
  214. case SigRotCW:
  215. changePage(PAGE_STATS2);
  216. break;
  217. case SigRotCCW:
  218. changePage(PAGE_TEMP);
  219. break;
  220. }
  221. break;
  222. case PAGE_STATS2:
  223. if (SigValueEmpty() && mode == MODE_MENU)
  224. pause();
  225. switch (getSigValue(MODE_MENU)) {
  226. case SigRotCW:
  227. changePage(PAGE_DESCALING);
  228. break;
  229. case SigRotCCW:
  230. changePage(PAGE_STATS);
  231. break;
  232. }
  233. break;
  234. case PAGE_DESCALING:
  235. if (SigValueEmpty() && mode == MODE_MENU)
  236. pause();
  237. switch (getSigValue(MODE_MENU)) {
  238. case SigRotCW:
  239. changePage(PAGE_EXIT);
  240. break;
  241. case SigRotCCW:
  242. changePage(PAGE_STATS2);
  243. break;
  244. }
  245. break;
  246. case PAGE_EXIT:
  247. if (SigValueEmpty() && mode == MODE_MENU)
  248. pause();
  249. switch (getSigValue(MODE_MENU)) {
  250. case SigInt0Rls:
  251. leaveMenu();
  252. break;
  253. case SigRotCW:
  254. if (state == STATE_HEATING || state == STATE_ERROR
  255. || state == STATE_IDLE
  256. || state == STATE_INITALHEATING) {
  257. changePage(PAGE_SOFTOFF);
  258. } else {
  259. changePage(PAGE_DEMO);
  260. }
  261. break;
  262. case SigRotCCW:
  263. changePage(PAGE_DESCALING);
  264. break;
  265. }
  266. break;
  267. } //end switch (page)
  268. /*
  269. * Hardware FSM
  270. */
  271. switch (state) {
  272. /*
  273. *
  274. */
  275. case STATE_OFF:
  276. if (mode == MODE_STATE) {
  277. halMachineOff();
  278. writeBackCache();
  279. //TODO this might be a bit confusing to change the page here even if the menu isnt actually displayed
  280. changePage(PAGE_DEMO);
  281. if (SigValueEmpty())
  282. pause();
  283. }
  284. switch (getSigValue(MODE_STATE)) {
  285. case SigInt0Rls:
  286. case SigPowerUp:
  287. //Check waterlevel in gray water tank
  288. //turn machine on
  289. halMachineOn();
  290. coffeeNap(1,0);
  291. if (halIsHeating() && !halProxSensorCovered()) { //check if System starts to heat when turned on
  292. changeState(STATE_INITALHEATING);
  293. } else if (!halIsHeating() && !halProxSensorCovered()) {
  294. changeState(STATE_IDLE);
  295. } else if (halProxSensorCovered()) {
  296. logger_error("Empty Tank please!\n");
  297. changeState(STATE_FULLTANK);
  298. }
  299. if (page != PAGE_SOFTOFF)
  300. changePage(PAGE_SOFTOFF); //the machine is on, the menu starts with the turning off page
  301. break;
  302. case SigRotCCW:
  303. case SigRotCW:
  304. //Enter the menu
  305. /* This should be not necessary!
  306. * if (page != PAGE_DEMO)
  307. changePage(PAGE_DEMO); //machine is off, the menu starts with the demo page*/
  308. enterMenu();
  309. break;
  310. }
  311. break;
  312. /*
  313. *
  314. */
  315. case STATE_WAIT_OFF:
  316. if (SigValueEmpty() && mode == MODE_STATE)
  317. pause();
  318. switch (getSigValue(MODE_STATE)) {
  319. case SigPressOpn:
  320. coffeeNap(1,0); //wait so no load will be switched
  321. coffeeIncreaseHeatingTime(halgetHeatingTime());
  322. changeState(STATE_OFF);
  323. break;
  324. case SigInt0Psh:
  325. case SigInt1Psh:
  326. case SigPowerUp:
  327. if (halProxSensorCovered()) {
  328. changeState(STATE_FULLTANK);
  329. } else if (initalHeating) {
  330. changeState(STATE_INITALHEATING);
  331. } else {
  332. changeState(STATE_HEATING);
  333. }
  334. break;
  335. }
  336. break;
  337. /*
  338. *
  339. */
  340. case STATE_INITALHEATING:
  341. initalHeating = true;
  342. if (SigValueEmpty() && mode == MODE_STATE)
  343. pause();
  344. switch (getSigValue(MODE_STATE)) {
  345. // case SigInt0RlsLong:
  346. // //Turn machine off again
  347. // coffeeIncreaseHeatingTime(halgetHeatingTime());
  348. // changeState(STATE_OFF);
  349. // break;
  350. //
  351. // case SigInt0Rls:
  352. // changeState(STATE_WAIT_OFF);
  353. // break;
  354. case SigProxCvrd:
  355. changeState(STATE_FULLTANK);
  356. break;
  357. case SigPressOpn:
  358. //Inital heating finished
  359. initalHeating = false;
  360. coffeeIncreaseHeatingTime(halgetHeatingTime());
  361. changeState(STATE_IDLE);
  362. break;
  363. case SigPowerDown:
  364. changeState(STATE_WAIT_OFF);
  365. break;
  366. case SigInt1Psh:
  367. enterMenu();
  368. break;
  369. }
  370. break;
  371. /*
  372. *
  373. */
  374. case STATE_HEATING:
  375. if (SigValueEmpty() && mode == MODE_STATE)
  376. pause();
  377. switch (getSigValue(MODE_STATE)) {
  378. // case SigInt1RlsLong:
  379. // //Turn machine _immediately_ off again
  380. // coffeeIncreaseHeatingTime(halgetHeatingTime());
  381. // changeState(STATE_OFF);
  382. // break;
  383. //
  384. // case SigInt1Rls:
  385. // //turn machine off when heating is finished
  386. // changeState(STATE_WAIT_OFF);
  387. // break;
  388. case SigPressOpn:
  389. coffeeIncreaseHeatingTime(halgetHeatingTime());
  390. changeState(STATE_IDLE);
  391. break;
  392. case SigInt0Psh:
  393. //start to brew a delicious coffee
  394. changeState(STATE_BREW);
  395. break;
  396. case SigProxCvrd:
  397. changeState(STATE_FULLTANK);
  398. break;
  399. case SigBrewOn:
  400. //someone brews manually
  401. changeState(STATE_BREWMANUAL);
  402. break;
  403. case SigPowerDown:
  404. changeState(STATE_WAIT_OFF);
  405. break;
  406. case SigInt1Psh:
  407. //Enter the menu
  408. enterMenu();
  409. break;
  410. }
  411. break;
  412. /*
  413. *
  414. */
  415. case STATE_IDLE:
  416. if (SigValueEmpty() && mode == MODE_STATE)
  417. pause();
  418. switch (getSigValue(MODE_STATE)) {
  419. // case SigInt1RlsLong:
  420. // //turn machine _immediately_ off
  421. // changeState(STATE_OFF);
  422. // break;
  423. //
  424. // case SigInt1Rls:
  425. // changeState(STATE_OFF);
  426. // break;
  427. case SigPressCls:
  428. changeState(STATE_HEATING);
  429. break;
  430. case SigInt0Psh:
  431. changeState(STATE_BREW);
  432. break;
  433. case SigProxCvrd:
  434. changeState(STATE_FULLTANK);
  435. break;
  436. case SigBrewOn:
  437. //someone brews manually
  438. changeState(STATE_BREWMANUAL);
  439. break;
  440. case SigPowerDown:
  441. changeState(STATE_OFF);
  442. break;
  443. case SigInt1Psh:
  444. //Enter the menu
  445. enterMenu();
  446. break;
  447. }
  448. break;
  449. /*
  450. *
  451. */
  452. case STATE_BREW:
  453. //make sure the tank is not full
  454. if (halProxSensorCovered()) {
  455. changeState(STATE_FULLTANK);
  456. logger_error("coffee.cpp: Full tank detection failed..\n");
  457. } else {
  458. coffeeBrew();
  459. checkDescaling();
  460. logger(V_BREW, "Finishing brewing\n");
  461. if (!halProxSensorCovered()) {
  462. if (halIsHeating()) {
  463. changeState(STATE_HEATING);
  464. } else {
  465. changeState(STATE_IDLE);
  466. }
  467. } else {
  468. changeState(STATE_FULLTANK);
  469. }
  470. }
  471. break;
  472. /*
  473. *
  474. */
  475. case STATE_BREWMANUAL:
  476. if (SigValueEmpty() && mode == MODE_STATE)
  477. pause();
  478. break;
  479. /*
  480. *
  481. */
  482. case STATE_CLEANING: //this can only be executed once the machine is hot!
  483. if (!halProxSensorCovered()) {
  484. //execute the cleaning procedure
  485. coffeeClean();
  486. if (halIsHeating()) {
  487. changeState(STATE_HEATING);
  488. } else {
  489. changeState(STATE_IDLE);
  490. }
  491. } else {
  492. changeState(STATE_FULLTANK);
  493. }
  494. break;
  495. /*
  496. * Full tank is detected at the beginning and the end of a brewing process, during
  497. * idle times, initial heating and heating
  498. */
  499. case STATE_FULLTANK:
  500. if (SigValueEmpty() && mode == MODE_STATE)
  501. pause();
  502. switch (getSigValue(MODE_STATE)) {
  503. case SigInt1Psh:
  504. case SigInt0Psh:
  505. if (halIsHeating() && initalHeating) {
  506. changeState(STATE_INITALHEATING);
  507. } else if (halIsHeating() && !initalHeating) {
  508. changeState(STATE_HEATING);
  509. } else {
  510. changeState(STATE_IDLE);
  511. }
  512. break;
  513. }
  514. break;
  515. /*
  516. *
  517. */
  518. case STATE_ERROR:
  519. if (SigValueEmpty() && mode == MODE_STATE)
  520. pause();
  521. switch (getSigValue(MODE_STATE)) {
  522. case SigInt1RlsLong:
  523. case SigInt0RlsLong:
  524. if (halIsHeating()) {
  525. coffeeIncreaseHeatingTime(halgetHeatingTime());
  526. }
  527. changeState(STATE_OFF);
  528. break;
  529. }
  530. }
  531. }
  532. pthread_exit(EXIT_SUCCESS);
  533. }
  534. /**
  535. * Handler for the Signal send to this thread
  536. * It saves the type of signal received and tracks the time between a push and release event of up to 4 signals
  537. * The time is stored in the HalEvent variable when a release event is received
  538. * @param signum
  539. * @param siginfo
  540. * @param context
  541. */
  542. void coffeeHandler(int signum, siginfo_t *siginfo, void *context) {
  543. sigval_t sigVal = (siginfo->si_value);
  544. sigValue = sigVal.sival_int;
  545. logger(V_BREW, "coffee.cpp: CoffeeHandler called with Signal %d\n",
  546. sigValue);
  547. }
  548. /**
  549. * returns the Signal value from the last received Signal for the given mode and clears the variable
  550. * @return value sent with the last signal
  551. */
  552. int getSigValue(coffee_mode_t mode) {
  553. int tmp = sigValue;
  554. if (mode == MODE_MENU) {
  555. switch (sigValue) {
  556. case SigInt0Psh:
  557. case SigInt0Rls:
  558. case SigInt0RlsLong:
  559. case SigInt1Psh:
  560. case SigInt1Rls:
  561. case SigInt1RlsLong:
  562. case SigRotCCW:
  563. case SigRotCW:
  564. sigValue = 0;
  565. menuTimeout = 0;
  566. return tmp;
  567. break;
  568. default:
  569. break;
  570. }
  571. } else { //State Mode
  572. sigValue = 0;
  573. return tmp;
  574. }
  575. //int tmp = sigValue;
  576. //sigValue = 0;
  577. //return tmp;
  578. return 0;
  579. }
  580. bool SigValueEmpty(void) {
  581. if (sigValue == 0)
  582. return true;
  583. else
  584. return false;
  585. }
  586. /**
  587. * Changes the state of the machine to newState
  588. * prints the change to the logger
  589. * @param newState
  590. */
  591. void changeState(coffee_status_t newState) {
  592. logger(V_BREW, "Changing state to %s\n", StateName[newState]);
  593. state = newState;
  594. event_trigger("statechange", &state, sizeof(state));
  595. }
  596. /*
  597. * Change Page to new menu page
  598. */
  599. void changePage(coffee_menuPage_t newPage) {
  600. logger(V_BREW, "Change Page to %s\n", PageName[newPage]);
  601. event_trigger("pagechange", &newPage, sizeof(newPage));
  602. page = newPage;
  603. }
  604. /*
  605. * Changes the mode of the machine to the given mode
  606. */
  607. void changeMode(coffee_mode_t newMode) {
  608. if (newMode == MODE_MENU)
  609. logger(V_BREW, "Changing to menu mode\n");
  610. else
  611. logger(V_BREW, "Changing to state mode\n");
  612. event_trigger("modechange", &newMode, sizeof(newMode));
  613. mode = newMode;
  614. }
  615. /*
  616. * leaving the menu
  617. * sets the start page for the next menu call to softoff
  618. */
  619. void leaveMenu(void) {
  620. logger(V_BREW, "Leaving the menu again\n");
  621. //leave the menu again
  622. changeMode(MODE_STATE);
  623. //change page to initial page
  624. changePage(PAGE_SOFTOFF);
  625. //stop the timeout counter
  626. menuTimeout = 0;
  627. menuTimer.stop();
  628. }
  629. /**
  630. * entering the menu
  631. * starts the timeoutcounter and changes the mode to the Menu Mode
  632. */
  633. void enterMenu(void) {
  634. logger(V_BREW, "Entering the menu\n");
  635. changeMode(MODE_MENU);
  636. menuTimeout = 0;
  637. menuTimer.start();
  638. }
  639. /**
  640. * Returns the current state of the FSM
  641. */
  642. coffee_status_t getState(void) {
  643. return state;
  644. }
  645. /**
  646. * Returns the local up-to-date brewcounter
  647. */
  648. uint16_t getBrewCounter(void) {
  649. return brewCounter;
  650. }
  651. /**
  652. * Returns the total heating time in seconds
  653. */
  654. uint64_t getTotalHeatingTime(void) {
  655. return totalHeatingTime;
  656. }
  657. /**
  658. * Returns the value of the brewcounter when the last descaling happened
  659. */
  660. uint16_t getDescBrewCounter (void) {
  661. return descBrewcount;
  662. }
  663. /**
  664. * Returns the raw time stamp when the last descaling happened
  665. */
  666. time_t * getDescTimestamp (void) {
  667. return &descRawTimestamp;
  668. }
  669. /**
  670. * Counter for the brew time
  671. * refresh every 200ms
  672. */
  673. void brewTimeHandler(void) {
  674. brewTime += 200;
  675. }
  676. /**
  677. * Counter for the menu timeout
  678. * When no input is coming from the user the machine leaves the menu automatically after MENUTIMEOUT seconds
  679. */
  680. void menuTimeHandler(void){
  681. menuTimeout += 200;
  682. if((menuTimeout/1000) >= MENUTIMEOUT) {
  683. leaveMenu();
  684. }
  685. }
  686. /**
  687. * handles program termination
  688. */
  689. void coffeeTerminate(event_t *event) {
  690. logger(V_BREW, "Coffee.cpp: Terminating\n");
  691. //stop brewing
  692. halRelaisOff(RELAIS_PUMP);
  693. brewTimer.stop();
  694. writeBackCache();
  695. }
  696. /**
  697. * coffeeNap does almost the same as sleep() but resumes to sleep after a signal got delivered
  698. * sec: seconds to sleep
  699. * nanosec: nanoseconds to sleep
  700. */
  701. void coffeeNap (uint64_t sec, uint64_t nanosec){
  702. struct timespec sleepTime;
  703. clock_gettime(CLOCK_REALTIME, &sleepTime);
  704. sleepTime.tv_sec += sec;
  705. sleepTime.tv_nsec += nanosec;
  706. //overflow
  707. if(sleepTime.tv_nsec >= 1000000000) {
  708. sleepTime.tv_nsec -= 1000000000;
  709. sleepTime.tv_sec++;
  710. }
  711. int errval = 0;
  712. do{
  713. errval = clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &sleepTime, NULL);
  714. }
  715. while(errval == EINTR);
  716. if(errval) logger_error("coffee.cpp: suspending thread failed with error %d\n", errval);
  717. }
  718. /**
  719. * Function to write back the values of the local copies
  720. * brewCounter and totalHeatingTime
  721. *
  722. */
  723. void writeBackCache(void) {
  724. logger(V_BREW, "Writing back cache...\n");
  725. logger(V_BREW, "Writing back brewCounter with %d\n", brewCounter);
  726. if (sqlSetConf(CFGbrewcounter, brewCounter)) {
  727. logger_error("coffee.cpp: Couldn't write brewcounter to database");
  728. return;
  729. }
  730. logger(V_BREW, "Writing back total heating Time with %lld sec\n", totalHeatingTime);
  731. if (sqlSetConf(CFGHeatingTime, totalHeatingTime)) {
  732. logger_error("coffee.cpp: Couldn't write heating time to database");
  733. return;
  734. }
  735. logger(V_BREW, "Writing back descaling brew counter %d\n", descBrewcount);
  736. if (sqlSetConf(CFGDescBrewCount, descBrewcount)) {
  737. logger_error("coffee.cpp: Couldn't write descaling brewcount to database");
  738. return;
  739. }
  740. struct tm * timeinfo;
  741. timeinfo = localtime(&descRawTimestamp);
  742. logger(V_BREW, "Writing back descaling timestamp %d-%d-%d %d:%d\n", timeinfo->tm_mday, timeinfo->tm_mon, timeinfo->tm_year+1900, timeinfo->tm_hour, timeinfo->tm_min);
  743. if (sqlSetConf(CFGDescTimestamp, (uint64_t)descRawTimestamp)) {
  744. logger_error("coffee.cpp: Couldn't write descaling timestamp to database");
  745. return;
  746. }
  747. }
  748. /*
  749. * Procedure for cleaning the machine
  750. */
  751. void coffeeClean(void) {
  752. logger(V_BREW, "Cleaning...\n");
  753. for (int i = 0; i < 20; i++) {
  754. halPumpOn();
  755. coffeeNap(3,0);
  756. halPumpOff();
  757. coffeeNap(15,0);
  758. }
  759. updateDescaling();
  760. descaling = false;
  761. event_trigger("descaling", &descaling, sizeof(bool));
  762. }
  763. /**
  764. * Brewing process
  765. */
  766. void coffeeBrew(void) {
  767. coffeeIncreaseBrewCounter();
  768. halIncreaseLogCycleCounter();
  769. /*
  770. * Preinfusion
  771. */
  772. logger(V_BREW, "Starting preinfusion...\n");
  773. halResetFlow();
  774. halPumpOn();
  775. brewTime = 0;
  776. brewTimer.start();
  777. while (halGetFlow() < AMOUNT_PREINFUSION && brewTime < TIME_PREINFUSION) {
  778. coffeeNap(0, 50000000);
  779. if (getSigValue(MODE_STATE) == SigInt0Psh){
  780. stopBrewing();
  781. return;
  782. }
  783. }
  784. stopBrewing();
  785. /*
  786. * Wait for coffee to soak in infused water
  787. */
  788. brewTimer.start();
  789. while (brewTime < TIME_SOAK) {
  790. coffeeNap(1, 100000000);
  791. if (getSigValue(MODE_STATE) == SigInt0Psh) {
  792. stopBrewing();
  793. return;
  794. }
  795. }
  796. stopBrewing();
  797. /*
  798. * Brewing the actual espresso
  799. */
  800. logger(V_BREW, "Starting infusion...\n");
  801. halPumpOn();
  802. brewTimer.start();
  803. while (brewTime < TIME_INFUSION && halGetFlow() < AMOUNT_DBLESPRESSO) {
  804. coffeeNap(1, 100000000);
  805. if (getSigValue(MODE_STATE) == SigInt0Psh){
  806. stopBrewing();
  807. break;
  808. }
  809. }
  810. stopBrewing();
  811. halIncreaseLogCycleCounter();
  812. return;
  813. //TODO: I want to see the total elapsed brewing time!!
  814. }
  815. /*
  816. * Wrapper function for the end of a brewing process
  817. * this function stops the pump, brewtimer and resets the flow and brew time to zero
  818. */
  819. void stopBrewing() {
  820. halPumpOff();
  821. brewTimer.stop();
  822. brewTime = 0;
  823. halResetFlow();
  824. }
  825. /**
  826. *
  827. */
  828. void coffeeIncreaseBrewCounter(void) {
  829. brewCounter++;
  830. }
  831. /**
  832. *
  833. */
  834. void coffeeIncreaseHeatingTime(uint64_t heatingTime) {
  835. totalHeatingTime += heatingTime;
  836. }
  837. /**
  838. * Checks if the descaling is necessary
  839. * uses descBrewcount and descTimestamp
  840. */
  841. void checkDescaling(){
  842. int16_t dirtyEspresso = checkDirtyEspresso ();
  843. int16_t dirtyTime = checkDirtyTime ();
  844. if(dirtyEspresso <= 0) {
  845. logger(V_BREW, "Descaling necessary due to quantity: %d\n", dirtyEspresso);
  846. descaling = true;
  847. event_trigger("descaling", &descaling, sizeof(bool));
  848. }
  849. if(dirtyTime <= 0) {
  850. logger(V_BREW, "Descaling necessary due to time in days: %d\n", dirtyTime);
  851. descaling = true;
  852. event_trigger("descaling", &descaling, sizeof(bool));
  853. }
  854. }
  855. /**
  856. * this function returns the remaining espressi to be brewed before the descaling event is fired
  857. * returns a positive integer when there are cups remaining
  858. * and a negative when the number of cups are exceeded
  859. * Number of cups after a descaling is defined with DIRTY_ESPRESSO
  860. */
  861. int16_t checkDirtyEspresso (void) {
  862. return descBrewcount + DIRTY_ESPRESSO - brewCounter;
  863. }
  864. /**
  865. * Returns the remaining days before the next descaling event is fired
  866. * returns a positive integer if there is time left and a negative one if the descaling time was exceeded
  867. */
  868. int16_t checkDirtyTime (void) {
  869. time_t rawtime;
  870. time(&rawtime);
  871. double diffseconds = difftime(rawtime, descRawTimestamp);
  872. diffseconds /= 24*60*60; //calculate the days
  873. return DIRTY_TIME - diffseconds;
  874. }
  875. /**
  876. * updates the corresponding variables after a descaling process
  877. */
  878. void updateDescaling(){
  879. descBrewcount = brewCounter;
  880. time_t newDesTimestamp;
  881. time(&newDesTimestamp);
  882. if(newDesTimestamp == -1){
  883. logger(V_BREW, "Whoops, couldn't retrieve new descaling timestamp\n");
  884. }
  885. else {
  886. descRawTimestamp = newDesTimestamp;
  887. }
  888. }