coffee.cpp 23 KB

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