├── README ├── fsm.c └── fsm.h /README: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gui13/FSM/5bb644027eb9decbd11311146d6ec86040348f7b/README -------------------------------------------------------------------------------- /fsm.c: -------------------------------------------------------------------------------- 1 | /* 2 | * fsm.c 3 | * 4 | * Created on: 3 août 2010 5 | * Author: gui13 6 | */ 7 | 8 | #include 9 | #include 10 | 11 | #include "fsm.h" 12 | 13 | #define FSM_MARKER 0xD1CEA5ED 14 | 15 | struct _fsm_transition_table 16 | { 17 | int marker; /* identifier to make sure that the FSM structure which is passed is a correct FSM */ 18 | fsm_transition_pf **fsm_table; 19 | unsigned int current_state; 20 | unsigned int fsm_max_event; 21 | unsigned int fsm_max_state; 22 | 23 | void *pv_data; 24 | }; 25 | 26 | 27 | int fsm_generate( fsm_transition *transitions, int transition_nb, unsigned int start_state, void *pv_data, fsm_transition_table **transition_table ) 28 | { 29 | /* Checks */ 30 | if(transitions && transition_nb && transition_table) 31 | { 32 | 33 | /* First step: determine the transition table size. We simply go through the FSM transitions to get the max values of state and events */ 34 | unsigned int max_state = 0; 35 | unsigned int max_event = 0; 36 | int i; 37 | 38 | for(i=0; i< transition_nb; i++) 39 | { 40 | if (transitions[i].fsm_state > max_state) 41 | max_state = transitions[i].fsm_state; 42 | if (transitions[i].fsm_event > max_event) 43 | max_event = transitions[i].fsm_event; 44 | printf("Transition: st %d : e %d -> cb %x\n",transitions[i].fsm_state,transitions[i].fsm_event, (unsigned int)transitions[i].fsm_cb); 45 | } 46 | printf("Max state: %d max event: %d\n", max_state+1, max_event+1); 47 | if(max_state && max_event && (start_state < max_state)) 48 | { 49 | /* Now that we know about the size, allocate the fsm_transition_table */ 50 | fsm_transition_table *table = calloc(1, sizeof(fsm_transition_table)); 51 | if(table) 52 | { 53 | table->marker = FSM_MARKER; 54 | table->fsm_max_event = max_event+1; 55 | table->fsm_max_state = max_state+1; 56 | table->current_state = start_state; 57 | table->pv_data = pv_data; 58 | 59 | table->fsm_table = calloc( 1, table->fsm_max_event * sizeof(fsm_transition_pf *) ); 60 | for(i=0; ifsm_max_event; i++) 61 | { 62 | 63 | table->fsm_table[i] = calloc( 1, table->fsm_max_state * sizeof(fsm_transition_pf) ); 64 | if(!table->fsm_table[i]) 65 | /* todo: free the allocated memory!! */ 66 | return FSM_ERR_NO_MEMORY; 67 | } 68 | 69 | if(table->fsm_table) 70 | { 71 | /* table is alloc'ed, now fill it in */ 72 | int k=0; 73 | for(k=0; k< transition_nb; k++) 74 | { 75 | fsm_transition *t = &(transitions[k]); 76 | printf("Adding transition: st %d : e %d -> cb %x\n",t->fsm_state,t->fsm_event, (unsigned int)t->fsm_cb); 77 | if(table->fsm_table[t->fsm_event][t->fsm_state] != NULL) 78 | printf("Warning: FSM Overwriting callback for state %d and event %d\n", t->fsm_state, t->fsm_event); 79 | table->fsm_table[t->fsm_event][t->fsm_state] = t->fsm_cb; 80 | } 81 | *transition_table = table; 82 | return FSM_NO_ERROR; 83 | } 84 | } 85 | else 86 | { 87 | return FSM_ERR_NO_MEMORY; 88 | } 89 | 90 | } 91 | else 92 | { 93 | return FSM_ERR_ARGUMENT_ERROR; 94 | } 95 | 96 | return FSM_NO_ERROR; 97 | } 98 | else 99 | { 100 | /* One of the arguments is bad */ 101 | return FSM_ERR_ARGUMENT_ERROR; 102 | } 103 | } 104 | 105 | int fsm_handle_event( fsm_transition_table *t, unsigned int event) 106 | { 107 | /* Check the marker and that the event corresponds to a valid event */ 108 | if( t && (t->marker == FSM_MARKER) 109 | && (event < t->fsm_max_event)) 110 | { 111 | fsm_transition_pf transition = t->fsm_table[event][t->current_state]; 112 | printf("Handling event %d (current state: %d)\n", event, t->current_state); 113 | if (transition) 114 | { 115 | /* There's a transition, handle it */ 116 | t->current_state = transition(event, t->pv_data); 117 | return FSM_NO_ERROR; 118 | } 119 | /* No transition for this event, notify caller */ 120 | return FSM_ERR_NO_TRANSITION; 121 | } 122 | else 123 | { 124 | /* Bad argument.. */ 125 | return FSM_ERR_ARGUMENT_ERROR; 126 | } 127 | } 128 | 129 | int fsm_free( fsm_transition_table *table) 130 | { 131 | if(table && table->marker == FSM_MARKER) 132 | { 133 | int i; 134 | for(i=0; ifsm_max_event; i++) 135 | if(table->fsm_table[i]) 136 | free(table->fsm_table[i]); 137 | free(table->fsm_table); 138 | free(table); 139 | return FSM_NO_ERROR; 140 | } 141 | else 142 | { 143 | return FSM_ERR_ARGUMENT_ERROR; 144 | } 145 | } 146 | 147 | int fsm_get_current_state( fsm_transition_table *t) 148 | { 149 | if(t && t->marker == FSM_MARKER ) 150 | return t->current_state; 151 | else { 152 | return FSM_ERR_ARGUMENT_ERROR; 153 | } 154 | 155 | } 156 | -------------------------------------------------------------------------------- /fsm.h: -------------------------------------------------------------------------------- 1 | /* 2 | * fsm.h 3 | * 4 | * Created on: 3 août 2010 5 | * Author: gui13 6 | */ 7 | 8 | #ifndef FSM_H_ 9 | #define FSM_H_ 10 | /* 11 | The idea behind this API is to have a prettier and faster way to describe a FSM. 12 | As of today, FSM basically consist 13 | - either of a double dimension table which size is (Nb of states) * (Nb of event). 14 | handler_cb table[i][k]={ 15 | event1 event2 event3 16 | stateX { handler1, NULL, NULL}, 17 | stateY { handler2, handler3, NULL} 18 | }; 19 | This minimizes the handling time of a particular {event, state} sequence, since 20 | accessing an element of this table is simply a matter of table[event][state]. 21 | - or a table that describes the state machine as a list of event/state/handler: 22 | {STATE_X, EVENT1, handler_1}, 23 | {STATE_Y, EVENT1, handler_2}, 24 | {STATE_Y, EVENT2, handler_3} 25 | This method is prettier and easier to read, but slow to handle, since you have 26 | to walk the table to handle an event. 27 | 28 | Here, I try to merge the two behaviors: 29 | - you define a "pretty" table that describes your state machine, 30 | - you use this API to dynamically create the "fast version" of the table. 31 | 32 | You can then use this table to make your state machine run. 33 | */ 34 | 35 | /* errors you can get when using the API */ 36 | typedef enum { 37 | FSM_NO_ERROR = -1, /* everything went fine */ 38 | 39 | FSM_ERR_NO_TRANSITION = -2, /* the {event,state} couple has no defined 40 | transition */ 41 | 42 | FSM_ERR_ARGUMENT_ERROR = -3, /* one of the arguments you passed is wrong */ 43 | 44 | FSM_ERR_NO_MEMORY = -4 /* problem allocating the memory for the 45 | transition table */ 46 | } fsm_error_t; 47 | 48 | 49 | /* opaque structure used when generating/using/freeing a transition table */ 50 | typedef struct _fsm_transition_table fsm_transition_table; 51 | 52 | /** 53 | * When the FSM receives a transition event, it calls a transition callback (if 54 | * a callback is defined for this particular {state,event} tuple). 55 | * 56 | * This transition callback receives the event of the FSM and is given an eventual 57 | * private data. 58 | * 59 | * The callback should return the new state in which the FSM should be placed. 60 | */ 61 | typedef int (*fsm_transition_pf)(int event, void *pv_data); 62 | 63 | 64 | /* The structure of a "transition": the state your FSM is in, the event, and the 65 | function that will handle this event. 66 | NOTE: 67 | It is IMPORTANT that you make your states and events start from 0 and grow 68 | incrementaly. Since the transition table is allocated following the max value 69 | of your states and the max value of your events, using big numbers will get you 70 | a BIG FSM. 71 | */ 72 | typedef struct _fsm_transition 73 | { 74 | unsigned int fsm_state; /* state where the FSM is right now */ 75 | unsigned int fsm_event; /* event received by the FSM */ 76 | fsm_transition_pf fsm_cb; /* transition to apply to the FSM, which returns 77 | the new FSM state */ 78 | } fsm_transition; 79 | 80 | 81 | /** 82 | Use this function to generate a fsm_transition_table from a list of 83 | fsm_transitions. 84 | You can give a default "starting" state, as well as a private data that will be 85 | passed on each transition. 86 | 87 | \param[in] transitions a pointer to the transitions you want to include in your FSM. 88 | \param[in] transition_nb the size of the table of transitions you pass 89 | \param[in] start_state the initial state in which the FSM is to be placed 90 | \param[in] transition_table the address of a pointer which will receive the 91 | generated FSM table identifier. 92 | */ 93 | int fsm_generate( fsm_transition *transitions, 94 | int transition_nb, 95 | unsigned int start_state, 96 | void *pv_data, 97 | fsm_transition_table **transition_table ); 98 | 99 | /** 100 | Use this function to deallocate an allocated FSM. 101 | \param[in] table the allocated FSM. 102 | \return FSM_ERR_ARGUMENT_ERROR if the pointer is invalid, FSM_NO_ERROR is all 103 | went fine. 104 | */ 105 | int fsm_free( fsm_transition_table *table); 106 | 107 | /* Use the transition table t to handle the event you pass. 108 | \param[in] t the transition table you generated 109 | \param[in] event the event you want to handle. 110 | \return FSM_NO_ERROR if no error happened, FSM_ERR_NO_TRANSITION if no 111 | transition exists for the current {state, event} couple, FSM_ERR_ARGUMENT_ERROR 112 | if one of the arguments is bad. */ 113 | int fsm_handle_event(fsm_transition_table *t, unsigned int event); 114 | 115 | int fsm_get_current_state( fsm_transition_table *t); 116 | 117 | #endif /* FSM_H_ */ 118 | --------------------------------------------------------------------------------