From 2376aeb614d69d7050209f858818bfa7d36be398 Mon Sep 17 00:00:00 2001 From: Jérôme Jutteau Date: Mon, 2 May 2011 23:32:51 +0200 Subject: digital/io: adapt FSM to new FSM system (AngFSM) --- digital/io/src/fsm.h | 648 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 544 insertions(+), 104 deletions(-) (limited to 'digital/io/src/fsm.h') diff --git a/digital/io/src/fsm.h b/digital/io/src/fsm.h index 2d0e1933..6b376d30 100644 --- a/digital/io/src/fsm.h +++ b/digital/io/src/fsm.h @@ -1,126 +1,566 @@ -#ifndef fsm_h -#define fsm_h -/* fsm.h - Finite State Machine code. */ -/* io - Input & Output with Artificial Intelligence (ai) support on AVR. {{{ - * - * Copyright (C) 2008 Nicolas Schodet - * - * APBTeam: - * Web: http://apbteam.org/ - * Email: team AT apbteam DOT org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - * }}} */ +/* + AngFSM - AngFSM - Almost Non Generated Finite State Machine + + Copyright 2011 Jerome Jutteau + +Contact: + * email: j.jutteau _AT_ gmail _DOT_ com + * website: http://fuu.im/angfsm/ -#ifndef HOST + This file is part of AngFSM. -# include + AngFSM is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. -#else + AngFSM is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with AngFSM. If not, see . + */ -# define PROGMEM -# define pgm_read_word(addr) (*(addr)) +#include +#ifndef _FSM_GEN_ +#define _FSM_GEN_ + +#ifndef FSM_NAME +#define FSM_NAME FSM #endif -/** - * The io board includes an FSM with several concurrent active states. - * - * The main loop tests for each possible event and generate the corresponding - * FSM events. For example: - * - * if (asserv_move_cmd_status () == success) - * fsm_handle_event (&ai_fsm, AI_EVENT_position_reached); - * - * Any unhandled event will be ignored. - * - * To start a sub machine from the top FSM, call the sub machine start - * function (for example move_start), which will set parameters, then trigger - * its start event. +#define XSTR(s) STR(s) +#define STR(s) #s +#define FSM_PASTE(a, b) a ## b +#define FSM_PASTE_EXPAND(a, b) FSM_PASTE (a, b) +#define FSM_PASTE3_EXPAND(a, b, c)\ + FSM_PASTE_EXPAND (a, FSM_PASTE_EXPAND (b, c)) +#define FIRST(first, others...) first + +#ifdef __AVR_ARCH__ +/* Include generated header. */ +#include XSTR (FSM_PASTE_EXPAND (fsm_, FSM_NAME)_gen.h) + +/* Disable init macro. */ +#define FSM_INIT + +/* Disable code generation macro. */ +#define FSM_GENERATE(arch, string) + +/* Disable FSM_IMPORT macro. */ +#define FSM_IMPORT(fsm_name) + +/** Immediatly handle an event by the fsm. */ +#define FSM_HANDLE(fsm, event) fsm_##fsm##_handle (FSM_EVENT_##fsm##_##event ) + +/** Says if an event can actually be handle or not by the fsm. */ +#define FSM_CAN_HANDLE(fsm, event) fsm_##fsm##_can_handle \ + (FSM_EVENT_##fsm##_##event) + +/** Reset fsm to it's initial state. */ +#define FSM_RESET(fsm) fsm_##fsm##_init (); + +/** Disable dot generation. */ +#define FSM_GEN_DOT(fsm) + +/** Disable state defining * - * The sub machine is expected to generate an event for the top machine to - * make it continue. + * Define states of the fsm, can be called several times to add other states. */ +#define FSM_STATES(states...) + +/** Disable event defining */ +#define FSM_EVENTS(events...) + +/** Disable macro. */ +#define FSM_START_WITH(starters...) + +#define FSM_TRANS(state, event, output_branches...)\ + FSM_PASTE3_EXPAND (fsm_, FSM_NAME,_branch_t)\ +FSM_PASTE3_EXPAND (fsm_, FSM_NAME,_trans_func_##state##_##event) () + +#define FSM_NEXT(state, event, branch...)\ + FSM_PASTE3_EXPAND (FSM_BRANCH_, FSM_NAME,_##state##_##event##_##branch) + +/** Transform an event in uint16_t. */ +#define FSM_EVENT(fsm, event) FSM_EVENT_##fsm##_##event + +/** Handle event from uint16_t. */ +#define FSM_HANDLE_VAR(fsm, event) fsm_##fsm##_handle (event) + +/* Can we handle event from uint16_t ? */ +#define FSM_CAN_HANDLE_VAR(fsm, event) fsm_##fsm##_can_handle (event) + +/* Time out macros. */ +#define FSM_TRANS_TIMEOUT(state, timeout, output_branches...)\ + FSM_TRANS (state, state##_TIMEOUT, output_branches) + +#define FSM_NEXT_TIMEOUT(state, branch...)\ + FSM_NEXT (state, state##_TIMEOUT, branch) + +#define FSM_HANDLE_TIMEOUT(fsm_name) fsm_##fsm_name##_handle_timeout () + +#else /* Compiling for HOST */ +#include + +/* Describe an event. */ +typedef struct +{ + /* Name of the event. */ + char *var_name; + /* Unique code of the event. */ + uint code; +} fsm_build_event_t; + +/* Chain of events. */ +typedef struct fsm_build_event_chain_t +{ + fsm_build_event_t event; + struct fsm_build_event_chain_t *next; +} fsm_build_event_chain_t; + +/* Describe a state. Same as event. */ +typedef fsm_build_event_t fsm_build_state_t; + +/* Chain of state */ +typedef struct fsm_build_state_chain_t +{ + fsm_build_state_t state; + struct fsm_build_state_chain_t *next; +} fsm_build_state_chain_t; + +/** + * Describe a branch. It is the output of a transition. + * Branches can be chained when there are several possible output state for + * one event. + */ +typedef struct fsm_build_branch_chain_t +{ + /* Pointer to the output state of the branch. */ + fsm_build_state_t *state; + /* Name of the branch. */ + char *name; + /* Pointer to the next branch when we have the choice of the output */ + struct fsm_build_branch_chain_t *next; +} fsm_build_branch_chain_t; + +/** + * Describe a transition. + * When an event occurs, an active state may react to this event an execute a + * transition. The transition function returns the new state which become + * active. */ +typedef struct +{ + /* Active state. */ + fsm_build_state_t *state; + /* Active event. */ + fsm_build_event_t *event; + /* Possible states the transition will return. */ + fsm_build_branch_chain_t *output_branches; +} fsm_build_trans_t; -#ifdef HOST -/** On host, check branches. */ -typedef u32 fsm_branch_t; -#else -/** On AVR, no check. */ -typedef u8 fsm_branch_t; -#endif +/* Chain of transitions. */ +typedef struct fsm_build_trans_chain_t +{ + fsm_build_trans_t trans; + struct fsm_build_trans_chain_t *next; +} fsm_build_trans_chain_t; -/** Transition function. */ -typedef fsm_branch_t (*fsm_transition_t) (void); +/* Pointer to a transition function. */ +typedef fsm_build_state_t* (*fsm_build_run_strans_func_t)(void); -/** Maximum number of active states. */ -#define FSM_ACTIVE_STATES_MAX 5 +/* Chain of transitions with associated function's pointer. */ +typedef struct fsm_trans_func_chain_t +{ + fsm_build_run_strans_func_t func; + fsm_build_trans_t trans; + struct fsm_trans_func_chain_t *next; +} fsm_trans_func_chain_t; -/** Context of an FSM. */ -struct fsm_t +/* Timeout structure. */ +typedef struct fsm_build_timeout_t { - /** Transition table. */ - const fsm_transition_t *transition_table; - /** Number of events, used to index the right transition. */ - u8 events_nb; - /** Initial states. */ - u8 states_init[FSM_ACTIVE_STATES_MAX]; - /** Current active states. */ - u8 states_active[FSM_ACTIVE_STATES_MAX]; - /** Nomber of active states. */ - u8 active_states_nb; - /** State timeout table. */ - const u16 *state_timeout_table; - /** Current active states timeout if not 0xffff. */ - u16 states_timeout[FSM_ACTIVE_STATES_MAX]; - /** Event to generate on state timeout. */ - u8 state_timeout_event; -#ifdef HOST - /** FSM name. */ - const char *name; - /** States names. */ - const char * const *states_names; - /** Events names. */ - const char * const *events_names; -#endif -}; -typedef struct fsm_t fsm_t; + uint timeout; + fsm_build_trans_t *trans; +} fsm_build_timeout_t; + +/* Chain of fsm_timeout_t. */ +typedef struct fsm_build_timeout_chain_t +{ + fsm_build_timeout_t timeout; + struct fsm_build_timeout_chain_t *next; +} fsm_build_timeout_chain_t; + +/* Data needed for fsm execution (for host). */ +typedef struct +{ + /* This table store pointers of aff all transition functions. The first + * dimension correspond to the events and the second to the states. If + * there is no transition of a event/state couple, NULL is set. This + * permit to react quickly to an event during execution but is not the + * best for memory cost. + */ + fsm_build_run_strans_func_t **trans_table; + /* Store pointer to active states. */ + fsm_build_state_t **active_states; + /* This array has the size of active_states and store the event who has + * triggered the corresponding active state. This is used for dot + * generation for visual purposes. + */ + fsm_build_event_t **events_before_active_state; + /* Store all pointers of transition functions. */ + fsm_trans_func_chain_t *func_pool; + /* Array of counters for timeout events. + * -1 mean counter is off. */ + int *timeout_counters; +} fsm_build_run_t; + +/* Store all Finite State Machine (fsm) informations. */ +typedef struct +{ + /* All events. */ + fsm_build_event_chain_t *events; + /* All states. */ + fsm_build_state_chain_t *states; + /* All transitions. */ + fsm_build_trans_chain_t *trans; + /* Name of the fsm. */ + char *name; + /* Maximal number of active states. */ + uint max_active_states; + /* Total number of events. */ + uint event_nb; + /* Total number of states. */ + uint state_nb; + /* First active states. */ + fsm_build_state_chain_t *starters; + /* All timeout. */ + fsm_build_timeout_chain_t *timeouts; + /* Data for running purposes. */ + fsm_build_run_t run; +} fsm_build_t; + +/* Store all fsm in a chain. */ +typedef struct fsm_build_chain_t +{ + fsm_build_t *fsm; + struct fsm_build_chain_t *next; +} fsm_build_chain_t; + +/* Store all fsm. */ +extern fsm_build_chain_t *fsm_build_all_fsm; +/* Function to initialize fsm_build_all_fsm. */ +void fsm_build_init_all_fsm () __attribute__((constructor(101))); + +/** Create the fsm and functions who will be executed before and after main + * function. This permits initilialization of the fsm. */ +extern fsm_build_t FSM_PASTE_EXPAND (fsm_, FSM_NAME); +void FSM_PASTE_EXPAND (fsm_build_init_, FSM_NAME)() __attribute__((constructor(102))); +void FSM_PASTE_EXPAND (fsm_build_run_init_, FSM_NAME)() __attribute__((constructor(107))); +void FSM_PASTE_EXPAND (fsm_build_free_, FSM_NAME)() __attribute__((destructor)); + +#define FSM_INIT\ + fsm_build_t FSM_PASTE_EXPAND (fsm_, FSM_NAME);\ +void FSM_PASTE_EXPAND (fsm_build_init_, FSM_NAME)()\ +{\ + fsm_build_init (FSM_PASTE_EXPAND (&fsm_, FSM_NAME), XSTR(FSM_NAME));\ +}\ +void FSM_PASTE_EXPAND (fsm_build_run_init_, FSM_NAME)()\ +{\ + fsm_build_run_init (& FSM_PASTE_EXPAND (fsm_, FSM_NAME));\ +}\ +void FSM_PASTE_EXPAND (fsm_build_free_, FSM_NAME)()\ +{\ + fsm_build_free (& FSM_PASTE_EXPAND (fsm_, FSM_NAME));\ +} + +#define FSM_IMPORT(fsm_name)\ + extern fsm_build_t fsm_##fsm_name; + +/** Generate source ou header files for the specific architecture */ +#define FSM_GENERATE(arch, string) fsm_build_gen (#arch, string); + +/** Immediatly handle an event by the fsm. */ +#define FSM_HANDLE(fsm, event) fsm_build_handle_string (&fsm_##fsm , #event) + +/** Says if an event can actually be handle or not by the fsm. */ +#define FSM_CAN_HANDLE(fsm, event) fsm_build_can_handle_string (&fsm_##fsm, #event) + +/** Reset fsm to it's initial state. */ +#define FSM_RESET(fsm) fsm_build_reset (&fsm_##fsm) + +/** Generate dot. */ +#define FSM_GEN_DOT(fsm) fsm_build_gen_dot (&fsm_##fsm) + +/** Define states of the fsm, can be called several times to add other states. */ +#define FSM_STATES(states...)\ + void FSM_PASTE3_EXPAND (fsm_build_states_, FSM_NAME, FIRST (states)) () __attribute__((constructor(103)));\ +void FSM_PASTE3_EXPAND (fsm_build_states_, FSM_NAME, FIRST (states)) ()\ +{\ + fsm_build_states (& FSM_PASTE_EXPAND (fsm_, FSM_NAME), #states);\ +} + +/** Define events of the fsm, can be called several times to add other events. */ +#define FSM_EVENTS(events...)\ + void FSM_PASTE3_EXPAND (fsm_build_events_, FSM_NAME, FIRST (events)) () __attribute__((constructor(103)));\ +void FSM_PASTE3_EXPAND (fsm_build_events_, FSM_NAME, FIRST (events)) ()\ +{\ + fsm_build_events (& FSM_PASTE_EXPAND (fsm_, FSM_NAME), #events);\ +} -#define FSM_TRANSITION(fsm, state, event) \ - ((fsm_transition_t) pgm_read_word ( \ - &(fsm)->transition_table[(state) * (fsm)->events_nb + (event)])) +/** Define wich state(s) will be active at the beginning. */ +#define FSM_START_WITH(starters...)\ + void FSM_PASTE3_EXPAND (fsm_build_start_with_, FSM_NAME, FIRST (starters))() __attribute__((constructor(104)));\ +void FSM_PASTE3_EXPAND (fsm_build_start_with_, FSM_NAME, FIRST (starters))()\ +{\ + fsm_build_start_with (& FSM_PASTE_EXPAND (fsm_, FSM_NAME), #starters);\ +} -/** Reset a FSM. */ +/** + * Define a transition by giving a state, an event and: + * - a event if the transition returns only one state. + * - branches if the transition returns different states. + * See examples for reel usage. + */ +#define FSM_TRANS(state, event, output_branches...)\ + fsm_build_state_t* FSM_PASTE3_EXPAND (fsm_trans_, FSM_NAME,_##state##_##event) ();\ +void FSM_PASTE3_EXPAND (fsm_build_trans_, FSM_NAME,_##state##_##event)() __attribute__((constructor(105)));\ +void FSM_PASTE3_EXPAND (fsm_build_trans_, FSM_NAME,_##state##_##event)()\ +{\ + fsm_build_trans (& FSM_PASTE_EXPAND(fsm_, FSM_NAME), #state, #event,\ +#output_branches,\ + & FSM_PASTE3_EXPAND (fsm_trans_, FSM_NAME,_##state##_##event));\ +}\ +fsm_build_state_t* FSM_PASTE3_EXPAND (fsm_trans_, FSM_NAME,_##state##_##event) () + +/** + * Used to return next state by giving the actual transition informations and + * the branch (if there are several branches). + * Not directly returning the state can avoid some errors. * + */ +#define FSM_NEXT(state, event, branch...)\ + fsm_build_get_next_state (& FSM_PASTE_EXPAND (fsm_, FSM_NAME), #state, #event, #branch) + +/** + * Define a transition when a state times out. + * You have to provide a state, a timeout value and: + * - a event if the transition returns only one state. + * - branches if the transition returns different states. + * See examples for reel usage. + */ +#define FSM_TRANS_TIMEOUT(state, timeout, output_branches...)\ + void FSM_PASTE3_EXPAND (fsm_build_timeout_, FSM_NAME,_##state##_TIMEOUT)() __attribute__((constructor(106)));\ +void FSM_PASTE3_EXPAND (fsm_build_timeout_, FSM_NAME,_##state##_TIMEOUT)()\ +{\ + fsm_build_timeout (& FSM_PASTE_EXPAND(fsm_, FSM_NAME), #state, XSTR(state##_TIMEOUT), timeout);\ +}\ +FSM_EVENTS (state##_TIMEOUT)\ +FSM_TRANS (state, state##_TIMEOUT, output_branches) + +/** + * Used to return next state after a timeout. + * Same as FSM_NEXT but without specifying an event. + */ +#define FSM_NEXT_TIMEOUT(state, branch...)\ + FSM_NEXT (state, state##_TIMEOUT, branch) + +/** Used to handle timeout events. */ +#define FSM_HANDLE_TIMEOUT(fsm_name) fsm_build_handle_timeout (& FSM_PASTE_EXPAND (fsm_, FSM_NAME)) + +/** Transform an event in uint16_t. */ +#define FSM_EVENT(fsm_name, event) fsm_build_get_event_code (& FSM_PASTE_EXPAND (fsm_, FSM_NAME), #event) + +/** Handle event from uint16_t. */ +#define FSM_HANDLE_VAR(fsm, event) fsm_build_handle_integer (& FSM_PASTE_EXPAND (fsm_, FSM_NAME), event) + +/* Can we handle event from uint16_t ? */ +#define FSM_CAN_HANDLE_VAR(fSM, event) fsm_build_can_handle_integer (& FSM_PASTE_EXPAND (fsm_, FSM_NAME), event) + +/** + * Parse a string who contains a list of arguments seperated by comma. + * It will create and store each parameters (without space and ") in an array + * and give the number of parsed arguments. + * \param string string to parse + * \param tab pointer to a table of strings where to store the array + * \param nb pointer where to store the number of parsed arguments + */ +void +fsm_build_arg_parse (char *string, char ***tab, int *nb); + +/** + * Free an array of strings generated by fsm_build_arg_parse. + * \param tab pointer to the array of strings + * \param nb number of arguments stored in the array. + */ +void +fsm_build_arg_free (char ***tab, int nb); + +/** + * This function is executed when a state is returned by a transition. + * \param fsm fsm + * \param trans transition where the return occurs + * \param branch branch to transition has chosen. + */ +void +fsm_build_print (fsm_build_t *fsm, + fsm_build_trans_t* trans, + fsm_build_branch_chain_t* branch); + +/** + * Test the fsm and search for errors. + */ +void +fsm_build_sanity_check (fsm_build_t *fsm); + +/** Reset the fsm to it's initial state. */ +void +fsm_build_reset (fsm_build_t *fsm); + +/** Generate the dot file of the actual fsm state. */ +void +fsm_build_gen_dot (fsm_build_t *fsm); + +/** Initialize the fsm. */ void -fsm_init (fsm_t *fsm); +fsm_build_init (fsm_build_t *fsm, char *name); -/** Handle state timeout, return non-zero if at least an event was handled. */ -uint8_t -fsm_handle_timeout (fsm_t *fsm); +/** Initialize the running data of the fsm. */ +void +fsm_build_run_init (fsm_build_t *fsm); + +/** + * Add some states to the fsm. + * \param fsm fsm + * \param states states separated by comma + **/ +void +fsm_build_states (fsm_build_t *fsm, char *states); + +/** + * Add some events to the fsm. + * \param fsm fsm + * \param events events separated by comma + **/ +void +fsm_build_events (fsm_build_t *fsm, char *events); + +/** Get event's pointer by giving it's name. */ +fsm_build_event_t* +fsm_build_get_event (fsm_build_t *fsm, char *event); + +/** Get state's pointer by giving it's name. */ +fsm_build_state_t* +fsm_build_get_state (fsm_build_t *fsm, char *state); + +/** Get event's pointer by giving it's code. */ +fsm_build_event_t* +fsm_build_get_event_by_code (fsm_build_t *fsm, uint event); + +/** Get state's pointer by giving it's code. */ +fsm_build_state_t* +fsm_build_get_state_by_code (fsm_build_t *fsm, uint state); + +/** Get event code as uint16_t */ +uint16_t +fsm_build_get_event_code (fsm_build_t *fsm, char *event); + +/** + * Add a transition to the fsm. + * \param fsm fsm + * \param state state who will be active for the transition + * \param event event to react + * \param output_branches list of output branches seperated by comma or a + * single state (if there is only one branche). + * For example: + * - If the transition return a single state, it will be "stateX" + * - If the transition can return different states, it will be + * "branch1, state1, branch2, state2, branch3, state3" [...] + * \param trans_func pointer to the transition function. + */ +void +fsm_build_trans (fsm_build_t *fsm, + char *state, + char *event, + char *output_branches, + fsm_build_run_strans_func_t trans_func); + +/* Create transition based on a timeout. + * \param fsm fsm + * \param state state who times out + * \param event name of the timeout (specific to this state and described in macro) + * \param timeout value of timeout + */ +void +fsm_build_timeout (fsm_build_t *fsm, char *state, char *event, uint timeout); + +/** + * Define with which states the fsm will start. + * \param fsm fsm + * \param starters list of states seperated by comma + */ +void +fsm_build_start_with (fsm_build_t *fsm, char *starters); + +/** Handle an event by the fsm. + * \param fsm fsm + * \param event event to handle with fsm + * \return zero if event is not handled at all, one otherwise + **/ +int +fsm_build_handle (fsm_build_t *fsm, fsm_build_event_t *event); +int +fsm_build_handle_string (fsm_build_t *fsm, char *event); +int +fsm_build_handle_integer (fsm_build_t *fsm, uint16_t event); + +/** + * Say if the event can be handled or not. + * \param fsm fsm + * \param event event to test with the fsm + * \return zero if the event can be handled, non-zero otherwhise + */ +int +fsm_build_can_handle (fsm_build_t *fsm, fsm_build_event_t *event); +int +fsm_build_can_handle_string (fsm_build_t *fsm, char *event); +int +fsm_build_can_handle_integer (fsm_build_t *fsm, uint16_t event); -/** Handle an event on the given FSM, return non-zero if at least an event was - * handled. */ -uint8_t -fsm_handle_event (fsm_t *fsm, uint8_t event); +/** + * Handle timeout events of the fsm. + * \param fsm fsm + * \return one if an event has been handled, zero otherwise + */ +int +fsm_build_handle_timeout (fsm_build_t *fsm); + +/** Give the state at the transition output. */ +fsm_build_state_t* +fsm_build_get_next_state (fsm_build_t *fsm, + char *state, + char *event, + char *branch); -/** Return non zero if event can be handled. This can be used if event - * generation requires expensive computations. */ -uint8_t -fsm_can_handle_event (fsm_t *fsm, uint8_t event); +/** Generate header file for target which provides more optimised version of + * fsm execution + * \param arch specify generated architecture + * \param embedded_strings ask to embed states and events strings (0 = not + * embedded) + */ +void +fsm_build_gen (char *arch, uint embedded_strings); +void +fsm_build_gen_avr_h (fsm_build_t *fsm, uint embedded_strings); +void +fsm_build_gen_avr_c (fsm_build_t *fsm, uint embedded_strings); + +/** Free fsm allocated data. */ +void +fsm_build_free (fsm_build_t *fsm); -#include "ai_fsm.h" +#endif /* Architectures */ -#endif /* fsm_h */ +#endif /* #ifdef _FSM_GEN_ */ -- cgit v1.2.3