coffee.cpp 24 KB

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