fsm.c

Go to the documentation of this file.
00001 
00009 /* Copyright: (c) 2007 DCE Eurobot Dragon Team
00010  *            CTU FEE - Department of Control Engineering
00011  * Contacts: Tran Duy Khanh <trandk1@fel.cvut.cz>
00012  * License: GNU GPL v.2
00013  */
00014 
00015 #include "fsm.h"
00016 #include <stdio.h>
00017 
00018 const char *fsm_common_event_str(enum fsm_common_events ev)
00019 {
00020 #define E(name) case EV_##name: return "EV_" #name;
00021         switch (ev) {
00022                 E(NOEVENT);
00023                 E(INIT);
00024                 E(EXIT);
00025                 E(STATE_ENTERED);
00026                 E(RETURN);
00027                 E(TIMEOUT);
00028                 E(TIMER);
00029         }
00030 #undef E
00031         return NULL;
00032 };
00033 
00035 int fsm_nop_state(struct robo_fsm *fsm)
00036 {
00037         return RC_WAIT;
00038 }
00039 
00040 static void gettimeofday_ts(struct timespec *ts)
00041 {
00042         struct timeval tv;
00043         gettimeofday(&tv, NULL);
00044         /* Convert from timeval to timespec */
00045         ts->tv_sec = tv.tv_sec;
00046         ts->tv_nsec = tv.tv_usec * 1000;
00047 }
00048 
00055 void __fsm_timespec_add_ms(struct timespec *ts, struct timespec *now, long ms)
00056 {
00057         struct timespec __now;
00058         if (!now) {
00059                 gettimeofday_ts(&__now);
00060                 now = &__now;
00061         }
00062         ts->tv_sec  = now->tv_sec + ms/1000;
00063         /* usec overflow hack */
00064         if ((now->tv_nsec+(ms%1000)*1000000)>1000000000L)
00065                 ts->tv_sec++;
00066         ts->tv_nsec = (now->tv_nsec + (ms%1000)*1000000)%1000000000L;
00067 }
00068 
00069 void __fsm_timespec_invalidate(struct timespec *ts)
00070 {
00071         ts->tv_sec = 0;
00072         ts->tv_nsec = 0;
00073 }
00074 
00075 static inline int timespec_valid(struct timespec *ts)
00076 {
00077         return (ts->tv_sec != 0 || ts->tv_nsec != 0);
00078 }
00079 
00080 static inline int timespec_cmp(struct timespec *ts1, struct timespec *ts2)
00081 {
00082         if (ts1->tv_sec < ts2->tv_sec)
00083                 return -1;
00084         else if (ts1->tv_sec > ts2->tv_sec)
00085                 return +1;
00086         else if (ts1->tv_nsec < ts2->tv_nsec)
00087                 return -1;
00088         else if (ts1->tv_nsec > ts2->tv_nsec)
00089                 return +1;
00090         else
00091                 return 0;
00092 }
00093 
00094 static fsm_event
00095 sem_wait_checked(struct robo_fsm *fsm)
00096 {
00097         int ret;
00098         fsm_event event;
00099         do {
00100                 ret = sem_wait(&fsm->event_sent);
00101                 if (ret != 0) {
00102                         if (errno == EINTR)
00103                                 continue;
00104                         perror("sem_wait");
00105                         continue;
00106                 }
00107         } while (0);
00108         event = fsm->sent_event;
00109         sem_post(&fsm->event_noticed);
00110         return event;
00111 }
00112 
00113 static fsm_event
00114 sem_timedwait_checked(sem_t *sem, struct timespec *abs_timeout,
00115                       struct robo_fsm *fsm, enum fsm_common_events timeout_event)
00116 {
00117         int ret;
00118         fsm_event event;
00119         do {
00120                 ret = sem_timedwait(sem, abs_timeout);
00121                 if (ret != 0) {
00122                         switch (errno) {
00123                                 case ETIMEDOUT:
00124                                         fsm->now = *abs_timeout;
00125                                         __fsm_timespec_invalidate(abs_timeout);
00126                                         event = timeout_event;
00127                                         break;
00128                                 case EINTR:
00129                                         continue;
00130                                 default:
00131                                         gettimeofday_ts(&fsm->now);
00132                                         perror("sem_timedwait");
00133                                         fprintf(stderr, "TIMER ERROR %d timer=%ld+%.3f\n", ret, fsm->timer.tv_sec, (double)fsm->timer.tv_nsec/1000000000.0);
00134                                         fprintf(stderr, "TIMER ERROR %d   now=%ld+%.3f\n\n\n", ret, fsm->now.tv_sec, (double)fsm->now.tv_nsec/1000000000.0);
00135                                         __fsm_timespec_invalidate(&fsm->timer);
00136                                         continue;
00137                         }
00138                 } else {
00139                         gettimeofday_ts(&fsm->now);
00140                         event = fsm->sent_event;
00141                         sem_post(&fsm->event_noticed);
00142                 }
00143         } while (0);
00144         return event;
00145 }
00146 
00147 static inline fsm_event
00148 wait_for_event(struct robo_fsm *fsm)
00149 {
00150         fsm_event event;
00151         if (!(timespec_valid(&fsm->timeout) || timespec_valid(&fsm->timer))) {
00152                 /* no timeout set */
00153                 event = sem_wait_checked(fsm);
00154                 gettimeofday_ts(&fsm->now);
00155         } else {
00156                 /* wait for timeout or timer */
00157                 if (timespec_valid(&fsm->timer) &&
00158                     (!timespec_valid(&fsm->timeout) ||
00159                      timespec_cmp(&fsm->timer, &fsm->timeout) <= 0)) {
00160                         /* wait for timer */
00161                         event = sem_timedwait_checked(&fsm->event_sent, &fsm->timer, fsm, EV_TIMER);
00162                 } else {
00163                         /* wait for timeout */
00164                         event = sem_timedwait_checked(&fsm->event_sent, &fsm->timeout, fsm, EV_TIMEOUT);
00165                 }
00166                 __fsm_timespec_invalidate(&fsm->timeout);
00167         }
00168         return event;
00169 }
00170 
00171 static inline void
00172 handle_event(struct robo_fsm *fsm, fsm_event event)
00173 {
00174         int ret_code;
00175 
00176         fsm->event = event;     /* Set what to handle */
00177 
00178         /* This does all the magic */
00179         if (fsm->state)
00180                 while ((ret_code = fsm->state(fsm)) != RC_WAIT);
00181         else
00182             fprintf(stderr, "FSM %s: fnc->act is NULL!!!\n", fsm->debug_name);
00183         fsm->event = EV_NOEVENT;
00184 }
00185 
00189 static void *
00190 fsm_thread_routine(void *arg)
00191 {
00192         struct robo_fsm *fsm = (struct robo_fsm *)arg;
00193         fsm_event event = EV_NOEVENT;
00194 
00195         while(event != EV_EXIT) {
00196                 event = wait_for_event(fsm);
00197                 handle_event(fsm, event);
00198         }
00199 
00200         return NULL;
00201 }
00202 
00212 void fsm_init(struct robo_fsm *fsm, char *debug_name)
00213 {
00214 
00215         /* Execute EV_INIT immediately after start. */
00216         fsm->sent_event = EV_INIT;
00217         sem_init(&fsm->event_sent, 0, 1);
00218 
00219         /* Until EV_INIT is processed, FSM cannot be signaled. See fsm_signal. */
00220         sem_init(&fsm->event_noticed, 0, 0);
00221 
00222         __fsm_timespec_invalidate(&fsm->timeout);
00223         __fsm_timespec_invalidate(&fsm->timer);
00224 
00225         /* initialize function stack and put first state into stack */
00226         fsm->fnc_sp = &fsm->fnc_stack[0];
00227 
00228         /* Inicialize with NOP state */
00229         fsm->state = &fsm_nop_state;
00230 
00231         /* debug data */
00232         fsm->debug_name = debug_name;
00233 }
00234 
00245 int fsm_start(struct robo_fsm *fsm, pthread_attr_t *attr)
00246 {
00247         int ret;
00248         ret = pthread_create(&fsm->threadid, attr,
00249                              fsm_thread_routine,
00250                              (void *)fsm);
00251         return ret;
00252 }
00253 
00262 int fsm_destroy(struct robo_fsm *fsm)
00263 {
00264         sem_destroy(&fsm->event_sent);
00265         sem_destroy(&fsm->event_noticed);
00266         return 0;
00267 }
00268 

Generated on Thu Sep 13 11:28:28 2007 for DCE-Eurobot by  doxygen 1.5.3