// Standalone example statemachine implementation and usage. Part of the // statemachine series on softwareprofessional.nl // // 16-03-2015 // /////////////////////////////////////////////////////////////////////////////// // INCLUDES /////////////////////////////////////////////////////////////////////////////// #include #include #include /////////////////////////////////////////////////////////////////////////////// // LOCAL MACROS /////////////////////////////////////////////////////////////////////////////// #define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0])) #define INVALID 0xFFu #define STATE( s ) { INVALID, NULL, NULL, (s) } #define DEFAULT_ON_LEVEL 128u #define MIN(a, b) ((a) < (b) ? (a) : (b)) #define MAX(a, b) ((a) > (b) ? (a) : (b)) #define CLIP(value, min, max) MIN(MAX((value), (min)), (max)) /////////////////////////////////////////////////////////////////////////////// // LOCAL TYPES /////////////////////////////////////////////////////////////////////////////// typedef uint8_t trigger_t; typedef uint8_t state_t; typedef bool (*guardHandler_t)(void); typedef void (*effectHandler_t)(void); typedef struct statemachineRule_t { trigger_t trigger; guardHandler_t guard; effectHandler_t effect; state_t state; } statemachineRule_t; typedef struct statemachine_t { const statemachineRule_t *rules; const uint8_t nrOfRules; state_t currentState; } statemachine_t; // triggers enum { tPressOn , tPressOff , tPressUp , tPressDown , tReleaseUp , tReleaseDown }; /////////////////////////////////////////////////////////////////////////////// // LOCAL FUNCTION PROTOTYPES /////////////////////////////////////////////////////////////////////////////// // guards static bool NONE(void); static bool gLevelIsZero(void); static bool ELSE(void); // effects static void eSetDefaultOnLevel(void); static void eTurnOff(void); static void eDimUp(void); static void eDimDown(void); static void eStopDimming(void); static void statemachine_ApplyTrigger( statemachine_t *pstatemachine , const trigger_t trigger ); static void applyTrigger(const trigger_t trigger); /////////////////////////////////////////////////////////////////////////////// // LOCAL CONSTANTS AND VARIABLES /////////////////////////////////////////////////////////////////////////////// static uint8_t s_dimLevel = DEFAULT_ON_LEVEL; static int8_t s_dimDelta = 0u; /////////////////////////////////////////////////////////////////////////////// // LOCAL FUNCTIONS /////////////////////////////////////////////////////////////////////////////// // guards static bool NONE(void) { return true; } static bool gLevelIsZero(void) { return (s_dimLevel == 0u); } static bool ELSE(void) { return true; } // effects static void eSetDefaultOnLevel(void) { s_dimLevel = DEFAULT_ON_LEVEL; printf("Set dimlevel to default on level.\n"); } static void eTurnOff(void) { s_dimLevel = 0u; printf("Turn lights off...\n"); } static void eDimUp(void) { s_dimDelta = 5; printf("Start dimming up.\n"); } static void eDimDown(void) { s_dimDelta = -3; printf("Start dimming down.\n"); } static void eStopDimming(void) { s_dimDelta = 0; printf("Stop dimming.\n"); } static void statemachine_ApplyTrigger( statemachine_t *pStatemachine, trigger_t trigger ) { uint8_t internalState = INVALID; for ( size_t i = 0u; i < pStatemachine->nrOfRules; ++i ) { statemachineRule_t rule = pStatemachine->rules[i]; if ( rule.trigger == INVALID ) { internalState = rule.state; continue; } if ( (internalState == pStatemachine->currentState) && (trigger == rule.trigger) && rule.guard() ) { rule.effect(); pStatemachine->currentState = rule.state; break; } } printf("current state : %hu\n", pStatemachine->currentState); } static void applyTrigger(const trigger_t trigger) { // states enum { sOff , sOn , sDimming }; static const statemachineRule_t rules[] = { // trigger , guard , effect , next state STATE( sOff ), { tPressOn , NONE , eSetDefaultOnLevel, sOn }, STATE( sOn ), { tPressOff , NONE , eTurnOff , sOff }, { tPressUp , NONE , eDimUp , sDimming }, { tPressDown , NONE , eDimDown , sDimming }, STATE( sDimming ), { tPressOff , NONE , eTurnOff , sOff }, { tReleaseUp , NONE , eStopDimming , sOn }, { tReleaseDown, gLevelIsZero, eTurnOff , sOff }, { tReleaseDown, ELSE , eStopDimming , sOn }, }; static statemachine_t statemachine = { rules , ARRAY_SIZE(rules), sOff }; statemachine_ApplyTrigger(&statemachine, trigger); } /////////////////////////////////////////////////////////////////////////////// // EXPORTED CONSTANTS AND VARIABLES /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // EXPORTED FUNCTIONS /////////////////////////////////////////////////////////////////////////////// int main(void) { printf( "usage :\n" " 1) Press On\n" " 0) Press Off\n" " +) Press/Release Up (toggle)\n" " -) Press/Release Down (toggle)\n" "\n" " x) exit\n"); bool upState = false; bool downState = false; for(;;) { int c = getchar(); if ( c < 0 ) break; if ( c == '\n' ) continue; switch( c ) { case '1' : applyTrigger(tPressOn) ; break; case '0' : applyTrigger(tPressOff); break; case '+' : applyTrigger( upState ? tReleaseUp : tPressUp ); upState = !upState; break; case '-' : applyTrigger( downState ? tReleaseDown : tPressDown ); downState = !downState; break; case 'x' : return 0; default : break; } // simulate dimming s_dimLevel = (uint8_t)CLIP((int)s_dimLevel + (int)s_dimDelta, 0, 255); printf("current dim level is %u\n", s_dimLevel); } return 0; }