[= AutoGen5 Template -*- Mode: text -*- h=%s-fsm.h c=%s-fsm.c (setenv "SHELL" "/bin/sh") (define fmt "") (shellf "[ -f %1$s-fsm.h ] && mv -f %1$s-fsm.h .fsm.head [ -f %1$s-fsm.c ] && mv -f %1$s-fsm.c .fsm.code" (base-name)) # AutoGen copyright 1992-2006 Bruce Korb =] [= CASE (suffix) =][=# PURPOSE: This template collection will produce a finite state machine based on a description of such a machine. The presumption is that you generally do not need to know what the current state is in order to determine what kind of transition is to be taken. That is to say, there is a global transition table that is indexed by the current state and the next transition type to determine the next state and trigger any optional transition handling code. The finite state machine may be either the master, driving the other parts of the program, or act as a subroutine keeping track of state between calls. Consequently, the "type" attribute may be set to: looping If the machine processes transitions until it reaches a terminal state (error or done). stepping If the FSM code will process a single transition and then return. reentrant This method is much the same as stepping, except that the caller must save the current state and provide it on the next call. In this fashion, an FSM may be used in a multi threaded application. The machine can be constructed in either of three formats, depending on the value of the "method" attribute: callout This method will use a callout table instead of a switch statement to implement the machine. case This is the alternate implementation method. none Do not supply the "method" attribute. Choosing this will cause only the dispatch table to be created. The implementation code is omitted. The "type" attribute is then ignored. YOU SUPPLY: "state" The list of valid state names. The "init" and "done" states are automatically added to this. If there are other terminal states, they must set "nxtSt" to "done". "event" The list of valid transition types. "type" The machine structure type: looping, stepping or reentrant "method" The implementation type: callout table, case statement or none. "prefix" A prefix to glue onto the front of created names "cookie" zero, one or more of these each containing a C type and name suitable for use in a procedure header. It is used to pass through arguments to implementation code. "transition" Define the handling for a transition from one state to another. It contains: "tst" the starting state(s). This may be one, or a list or '*' to indicate all states. "tev" the event that triggers this transition. This may also be a list of events or a '*'. "ttype" the transition type. By default it is named after the state and event names, but by specifying a particular type, multiple different transitions may invoke the same handling code. "next" the presumptive destination state. "presumptive" because the code that handles the transition may select a different destination. Doing that will violate mathematical models, but it often makes writing this stuff easier. THE TEMPLATE PRODUCES: a header file enumerating the states and events, and declaring the FSM state machine procedure. the code file with the implementation (provided "method" was specified). This source file contains special comments around code that is to be preserved from one regeneration to the next. BE VERY CAREFUL: if you change the name of a state or event, any code that was stored under the old name will not be carried forward and is likely to get lost. If you change names, then *save your work*. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ =][= == h =][= INCLUDE "fsm-trans.tpl" =][= INCLUDE "fsm-macro.tpl" =][= INVOKE preamble =] /* * This file enumerates the states and transition events for a FSM. * * te_[=(. pfx)=]_state * The available states. FSS_INIT is always defined to be zero * and FSS_INVALID and FSS_DONE are always made the last entries. * * te_[=(. pfx)=]_event * The transition events. These enumerate the event values used * to select the next state from the current state. * [=(. PFX)=]_EV_INVALID is always defined at the end. */ [=(make-header-guard "autofsm")=] /* * Finite State machine States * * Count of non-terminal states. The generated states INVALID and DONE * are terminal, but INIT is not :-). */ #define [=(. PFX)=]_STATE_CT [=(+ 1 (count "state"))=] typedef enum { [= (shellf "${CLexe-columns} --spread=1 -I4 -S, -f'%s_ST_%%s' <<_EOF_ INIT %s INVALID DONE _EOF_" PFX (string-upcase! (join "\n" (stack "state"))) )=] } te_[=(. pfx)=]_state; /* * Finite State machine transition Events. * * Count of the valid transition events */ #define [=(. PFX)=]_EVENT_CT [=(count "event")=] typedef enum { [= compute-transitions =][= (shellf "${CLexe-columns} --spread=1 -I4 -S, -f'%s_EV_%%s' <<_EOF_ %s INVALID _EOF_" PFX (string-upcase! (join "\n" (stack "event"))) )=] } te_[=(. pfx)=]_event; [= CASE method =][= ~* call|case =][= # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # We are implementing the machine. Declare the external =][= CASE type =][= ~* step|reent =][= make-step-proc mode = "extern " =];[= =* loop =][= make-loop-proc mode = "extern " =];[= * =][= (error (string-append "invalid FSM type: ``" (get "type") "'' must be ``looping'', ``stepping'' or ``reentrant''" )) =][= ESAC =][= # End external procedure declarations # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # We are *NOT* implementing the machine. Define the table =][= == "" =][= enumerate-transitions use_ifdef = yes =][= =* no =][= enumerate-transitions use_ifdef = yes =][= * =][= (error (sprintf "invalid FSM method: ``%s'' must be ``callout'', ``case'' or ``none''" (get "method"))) =][= ESAC =] #endif /* [=(. header-guard)=] */[= # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C OUTPUT BEGINS HERE # =][= == c =][= (if (~ (get "method") "(no.*){0,1}") (begin (shell "rm -f .fsm.*") (out-delete)) ) =][= INVOKE preamble =] #define DEFINE_FSM #include "[=(. header-file)=]" #include <stdio.h> /* * Do not make changes to this file, except between the START/END * comments, or it will be removed the next time it is generated. */ [=(extract fsm-source "/* %s === USER HEADERS === %s */")=] #ifndef NULL # define NULL 0 #endif [= CASE method =][= =* "case" =][= enumerate-transitions =][= =* "call" =][= callback-transitions =][= ESAC =] [=IF (=* (get "type") "step")=] /* * The FSM machine state */ static te_[=(. pfx)=]_state [=(. pfx)=]_state = [=(. PFX)=]_ST_INIT; [=ENDIF=] [= emit-invalid-msg =][= IF (=* (get "method") "call") =][= `set -- \`sed 's/,//' .fsm.xlist\`` =][= WHILE `echo $#` =][= INVOKE build-callback cb_prefix = (string-append pfx "_do") cb_name = (shell "echo $1 ; shift") =][= ENDWHILE echo $# =][= ENDIF =][= CASE type =][= =* loop =][= looping-machine =][= ~* step|reent =][= stepping-machine =][= ESAC =][= `rm -f .fsm.*` =][= ESAC (suffix) =] /* * Local Variables: * mode: C * c-file-style: "stroustrup" * tab-width: 4 * indent-tabs-mode: nil * End: * end of [=(out-name)=] */