/* * coffee.cpp * * Created on: Sep 25, 2017 * Author: sebastian */ #include #include #include #include #include #include #include #include #include #include #include #include #include "coffee.h" #include "hal.h" #include "logger.h" #include "timer.h" #include "database.h" #include "display.h" coffee_status_t state; int sigValue; int brewTime; //Brew time in ms timer brewTimer(&brewTimeHandler); clock_t beginHeating; clock_t endHeating; double heatingTime; /** * 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; heatingTime = 0; beginHeating = endHeating = clock(); 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 { 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: if (SigValueEmpty()) pause(); if (getSigValue() == SigInt0Rls) { if (halProxSensorCovered()) { //Check Waterlevel //turn machine on halMachineOn(); sleep(1); if (halIsHeating()) { //check if System starts to heat when turned on changeState(STATE_INITALHEATING); } else { changeState(STATE_IDLE); } } else { changeState(STATE_ERROR); } break; } break; /* * */ case STATE_INITALHEATING: beginHeating = clock(); if (SigValueEmpty()) pause(); switch (getSigValue()) { case SigInt1RlsLong: //Turn machine off again halMachineOff(); changeState(STATE_OFF); break; case SigPressOpn: //Inital heating finished changeState(STATE_IDLE); break; } endHeating = clock(); heatingTime = double(endHeating - beginHeating) / CLOCKS_PER_SEC; coffeeIncreaseHeatingTime((uint64_t)heatingTime); break; /* * */ case STATE_HEATING: beginHeating = clock(); if (SigValueEmpty()) pause(); switch (getSigValue()) { case SigInt1RlsLong: //Turn machine off again halMachineOff(); changeState(STATE_OFF); break; case SigPressOpn: changeState(STATE_IDLE); break; case SigInt0Psh: //start to brew a delicious coffee changeState(STATE_BREW); break; case SigBrewOn: //someone brews manually changeState(STATE_BREWMANUAL); break; } endHeating = clock(); heatingTime = double(endHeating - beginHeating) / CLOCKS_PER_SEC; coffeeIncreaseHeatingTime((uint64_t)heatingTime); break; /* * */ case STATE_IDLE: if (SigValueEmpty()) pause(); switch (getSigValue()) { case SigInt1RlsLong: //Turn machine off again halMachineOff(); changeState(STATE_OFF); break; case SigPressCls: changeState(STATE_HEATING); break; case SigInt0Psh: //start to brew a delicious coffee changeState(STATE_BREW); break; case SigBrewOn: //someone brews manually changeState(STATE_BREWMANUAL); break; } break; /* * */ case STATE_BREW: coffeeBrew(); logger(V_BREW, "Finishing brewing\n"); changeState(STATE_IDLE); break; /* * */ case STATE_BREWMANUAL: if (SigValueEmpty()) pause(); break; /* * */ case STATE_CLEANING: if (SigValueEmpty()) pause(); break; /* * */ case STATE_ERROR: if (SigValueEmpty()) pause(); 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, "CoffeeHandler called with %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 %d\n", newState); state = newState; displayPushState(newState); } /** * 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(void) { logger_error("Coffee thread terminated"); //stop brewing halRelaisOff(RELAIS_PUMP); brewTimer.stop(); } /** * 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; changeState(STATE_IDLE); return; } /** * */ void coffeeIncreaseBrewCounter(void) { uint64_t brewcounter = sqlGetConf(CFGbrewcounter); if (sqlSetConf(CFGbrewcounter, ++brewcounter)) { logger_error("Couldn't write brewcounter to database"); } } /** * */ void coffeeIncreaseHeatingTime(uint64_t heatingTime) { uint64_t totalHeatingTime = sqlGetConf(CFGHeatingTime); if (sqlSetConf(CFGHeatingTime, (totalHeatingTime + heatingTime))) { logger_error("Couldn't write heating time to database"); } }