123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485 |
- /*
- * coffee.cpp
- *
- * Created on: Sep 25, 2017
- * Author: sebastian
- */
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <errno.h>
- #include <stdint.h>
- #include <wiringPi.h>
- #include <pthread.h>
- #include <unistd.h>
- #include <iostream>
- #include <csignal>
- #include <time.h>
- #include <ctime>
- #include "coffee.h"
- #include "hal.h"
- #include "logger.h"
- #include "timer.h"
- #include "database.h"
- coffee_status_t state;
- int sigValue;
- int brewTime; //Brew time in ms
- timer brewTimer(&brewTimeHandler);
- uint64_t totalHeatingTime; //local copies of the corresponding database entries
- uint16_t brewCounter;
- bool initalHeating;
- const char* StateName[] = {"OFF", "HEATING", "INITHEAT", "IDLE", "BREW", "BREWMAN", "CLEAN", "ERROR", "WAITOFF"};
- /**
- * Thread for the finite state machine
- * It represents the current state of the machine and handles signals coming from
- * the pressure control, buttons, the brew switch and the proximity sensor
- * @param threadid the ID of the thread
- */
- void *coffeeThread(void *threadid) {
- logger(V_BASIC, "Initializing coffee thread...\n");
- //installing new Signal handler for coffeethread
- struct sigaction action;
- sigemptyset(&action.sa_mask);
- action.sa_flags = SA_SIGINFO;
- action.sa_sigaction = coffeeHandler;
- sigaction(SIGUSR2, &action, NULL);
- brewTimer.setDivider(4);
- brewTimer.stop();
- brewTime = 0;
- lastState = STATE_OFF;
- initalHeating = true;
- //read the database values
- if(!(totalHeatingTime = sqlGetConf(CFGHeatingTime))){
- logger_error("coffee.cpp: Couldn't read the heating time from the database");
- //pthread_exit(EXIT_SUCCESS);
- exit(EXIT_FAILURE);
- }
- if(!(brewCounter = sqlGetConf(CFGbrewcounter))){
- logger_error("coffee.cpp: Couldn't read the brew counter from the database");
- //pthread_exit(EXIT_SUCCESS);
- exit(EXIT_FAILURE);
- }
- event_subscribe("terminate", &coffeeTerminate);
- logger(V_BREW, "Determining inital state\n");
- //determine inital state
- if (halGetRelaisState(RELAIS_POWER) && halGetRelaisState(RELAIS_HEAT)
- && !halGetRelaisState(RELAIS_PUMP)) {
- //wait for heat relais to switch
- sleep(1);
- if (halIsHeating()) { //Heating is on
- changeState(STATE_INITALHEATING);
- } else {
- initalHeating = false;
- changeState(STATE_IDLE);
- }
- } else if (halGetRelaisState(RELAIS_PUMP)) {
- logger_error("Whoops, why is the pump running...\n");
- changeState(STATE_ERROR);
- } else {
- changeState(STATE_OFF);
- }
- logger(V_BREW, "Starting Coffee FSM\n");
- //begin FSM
- while (1) {
- switch (state) {
- /*
- *
- */
- case STATE_OFF:
- halMachineOff();
- writeBackCache();
- if (SigValueEmpty())
- pause();
- if (getSigValue() == SigInt0Rls) {
- //Check waterlevel in gray water tank
- //turn machine on
- halMachineOn();
- sleep(1);
- if (halIsHeating()) { //check if System starts to heat when turned on
- initalHeating = true;
- changeState(STATE_INITALHEATING);
- } else {
- changeState(STATE_IDLE);
- }
- if (halProxSensorCovered()) {
- logger_error("Empty Tank please!\n");//change the state again to go back to the last state afterwards
- changeState(STATE_FULLTANK);
- }
- break;
- }
- break;
- /*
- *
- */
- case STATE_WAIT_OFF:
- if (SigValueEmpty())
- pause();
- switch (getSigValue()) {
- case SigPressOpn:
- usleep(100000);//wait so no load will be switched
- coffeeIncreaseHeatingTime(halgetHeatingTime());
- changeState(STATE_OFF);
- break;
- case SigInt0Psh:
- case SigInt1Psh:
- if (halProxSensorCovered()) {
- changeState(STATE_FULLTANK);
- } else if (initalHeating) {
- changeState(STATE_INITALHEATING);
- } else {
- changeState(STATE_HEATING);
- }
- break;
- }
- break;
- /*
- *
- */
- case STATE_INITALHEATING:
- initalHeating = true;
- if (SigValueEmpty())
- pause();
- switch (getSigValue()) {
- case SigInt1RlsLong:
- //Turn machine off again
- coffeeIncreaseHeatingTime(halgetHeatingTime());
- changeState(STATE_OFF);
- break;
- case SigInt1Rls:
- changeState(STATE_WAIT_OFF);
- break;
- case SigProxCvrd:
- changeState(STATE_FULLTANK);
- break;
- case SigPressOpn:
- //Inital heating finished
- initalHeating = false;
- coffeeIncreaseHeatingTime(halgetHeatingTime());
- changeState(STATE_IDLE);
- break;
- }
- break;
- /*
- *
- */
- case STATE_HEATING:
- if (SigValueEmpty())
- pause();
- switch (getSigValue()) {
- case SigInt1RlsLong:
- //Turn machine _immediately_ off again
- coffeeIncreaseHeatingTime(halgetHeatingTime());
- changeState(STATE_OFF);
- break;
- case SigInt1Rls:
- //turn machine off when heating is finished
- changeState(STATE_WAIT_OFF);
- break;
- case SigPressOpn:
- coffeeIncreaseHeatingTime(halgetHeatingTime());
- changeState(STATE_IDLE);
- break;
- case SigInt0Psh:
- //start to brew a delicious coffee
- changeState(STATE_BREW);
- break;
- case SigProxCvrd:
- changeState(STATE_FULLTANK);
- break;
- case SigBrewOn:
- //someone brews manually
- changeState(STATE_BREWMANUAL);
- break;
- }
- break;
- /*
- *
- */
- case STATE_IDLE:
- if (SigValueEmpty())
- pause();
- switch (getSigValue()) {
- case SigInt1RlsLong:
- //turn machine _immediately_ off
- changeState(STATE_OFF);
- break;
- case SigInt1Rls:
- changeState(STATE_OFF);
- break;
- case SigPressCls:
- changeState(STATE_HEATING);
- break;
- case SigInt0Psh:
- changeState(STATE_BREW);
- break;
- case SigProxCvrd:
- changeState(STATE_FULLTANK);
- break;
- case SigBrewOn:
- //someone brews manually
- changeState(STATE_BREWMANUAL);
- break;
- }
- break;
- /*
- *
- */
- case STATE_BREW:
- //make sure the tank is not full
- if (halProxSensorCovered()) {
- changeState(STATE_FULLTANK);
- logger_error("coffee.cpp: Full tank detection failed..\n");
- } else {
- coffeeBrew();
- logger(V_BREW, "Finishing brewing\n");
- if (!halProxSensorCovered()) {
- if (halIsHeating()) {
- changeState(STATE_HEATING);
- } else {
- changeState(STATE_IDLE);
- }
- } else {
- changeState(STATE_FULLTANK);
- }
- }
- break;
- /*
- *
- */
- case STATE_BREWMANUAL:
- if (SigValueEmpty())
- pause();
- break;
- /*
- *
- */
- case STATE_CLEANING:
- if (SigValueEmpty())
- pause();
- break;
- /*
- * Full tank is detected at the beginning and the end of a brewing process, during
- * idle times, initial heating and heating
- */
- case STATE_FULLTANK:
- if (SigValueEmpty())
- pause();
- switch (getSigValue()) {
- case SigInt1Psh:
- case SigInt0Psh:
- if(halIsHeating() && initalHeating){
- changeState(STATE_INITALHEATING);
- } else if (halIsHeating() && !initalHeating){
- changeState(STATE_HEATING);
- } else {
- changeState(STATE_IDLE);
- }
- break;
- }
- break;
- /*
- *
- */
- case STATE_ERROR:
- if (SigValueEmpty())
- pause();
- switch (getSigValue()) {
- case SigInt1RlsLong:
- case SigInt0RlsLong:
- if(halIsHeating()){
- coffeeIncreaseHeatingTime(halgetHeatingTime());
- }
- changeState(STATE_OFF);
- break;
- }
- }
- }
- pthread_exit(EXIT_SUCCESS);
- }
- /**
- * Handler for the Signal send to this thread
- * It saves the type of signal received and tracks the time between a push and release event of up to 4 signals
- * The time is stored in the HalEvent variable when a release event is received
- * @param signum
- * @param siginfo
- * @param context
- */
- void coffeeHandler(int signum, siginfo_t *siginfo, void *context) {
- sigval_t sigVal = (siginfo->si_value);
- sigValue = sigVal.sival_int;
- logger(V_BREW, "coffee.cpp: CoffeeHandler called with Signal %d\n", sigValue);
- }
- /**
- * returns the Signal value from the last received Signal and clears the variable
- * @return value sent with the last signal
- */
- int getSigValue(void) {
- int tmp = sigValue;
- sigValue = 0;
- return tmp;
- }
- bool SigValueEmpty(void) {
- if (sigValue == 0)
- return true;
- else
- return false;
- }
- /**
- * Changes the state of the machine to newState
- * prints the change to the logger
- * @param newState
- */
- void changeState(coffee_status_t newState) {
- logger(V_BREW, "Changing state to %s\n", StateName[newState]);
- state = newState;
- event_trigger("statechange", &state, sizeof(state));
- }
- /**
- * Returns the current state of the FSM
- */
- coffee_status_t getState(void) {
- return state;
- }
- /**
- * Counter for the brew time
- * refresh every 200ms
- */
- void brewTimeHandler(void) {
- brewTime += 200;
- }
- /**
- * handles program termination
- */
- void coffeeTerminate(event_t *event) {
- logger(V_BREW, "Coffee.cpp: Thread terminating");
- //stop brewing
- halRelaisOff(RELAIS_PUMP);
- brewTimer.stop();
- writeBackCache();
- }
- /**
- * Function to write back the values of the local copies
- * brewCounter and totalHeatingTime
- *
- */
- void writeBackCache(void){
- if (sqlSetConf(CFGbrewcounter, brewCounter)) {
- logger_error("coffee.cpp: Couldn't write brewcounter to database");
- return;
- }
- if (sqlSetConf(CFGHeatingTime, totalHeatingTime)) {
- logger_error("coffee.cpp: Couldn't write heating time to database");
- return;
- }
- }
- /**
- * Brewing process
- */
- void coffeeBrew(void) {
- coffeeIncreaseBrewCounter();
- /*
- * Preinfusion
- */
- logger(V_BREW, "Starting preinfusion...\n");
- halResetFlow();
- halRelaisOn(RELAIS_PUMP);
- brewTime = 0;
- brewTimer.start();
- while (halGetFlow() < AMOUNT_PREINFUSION) {
- usleep(50000);
- if (getSigValue() == SigInt1Psh)
- return;
- }
- brewTimer.stop();
- brewTime = 0;
- halRelaisOff(RELAIS_PUMP);
- /*
- * Wait for coffee to soak in infused water
- */
- brewTimer.start();
- while (brewTime < TIME_SOAK) {
- usleep(100000);
- if (getSigValue() == SigInt1Psh)
- return;
- }
- brewTimer.stop();
- brewTime = 0;
- halResetFlow();
- /*
- * Brewing the actual espresso
- */
- logger(V_BREW, "Starting infusion...\n");
- halRelaisOn(RELAIS_PUMP);
- brewTimer.start();
- while (brewTime < TIME_INFUSION && halGetFlow() < AMOUNT_DBLESPRESSO) {
- usleep(100000);
- if (getSigValue() == SigInt1Psh)
- return;
- }
- halRelaisOff(RELAIS_PUMP);
- halResetFlow();
- brewTimer.stop();
- brewTime = 0;
- return;
- }
- /**
- *
- */
- void coffeeIncreaseBrewCounter(void) {
- brewCounter++;
- }
- /**
- *
- */
- void coffeeIncreaseHeatingTime(uint64_t heatingTime) {
- totalHeatingTime += heatingTime;
- }
|