- Description
- Synopsis
- Syntax
- Configurable Attributes - CFG Block
- State Machine - SM and IN Blocks
- Internals - fgen internals
- Bugs
- Files
fgen takes a system specification and generates code based on the specification. The idea is a programmer can make a simple description of something and then generate a lot of infrastructure type code based on the specification. fgen also makes it easy to add new system specifications, extensions to existing specifications, and different code generators for specifications.
Systems can be anything, but they usually map to something relatively complex where automatic code generation can provide a big productivity win. State machines are one such area where a state machine can be simply specified and a lot of code generated.
fgen is not a perfect compiler implementing a perfect grammar. It is a quick and simple way to describe things and generate code from the description. That's all. Please don't be too dissapointed :-)
fgen automatically generates code from simple system specifications. Currently fgen understands the following system specifications:
Fgen is designed to make it very easy to add new system specifications and parsers.
Fgen is a perl script which requires Perl5 to be installed on your system.
Fgen should be in your system's search path. Add the directory fgen is in to your system's PATH environment variable. Environment variables are set using Start/Setting/System/Environment.
perl fgen.pl files...
Perl is a call to the perl program that should be installed. If perl is not in your path then the full path to perl must be specified on the command line.
fgen.pl is the program that reads your specifications and generates code. If it is not in the current directory then the full path to fgen.pl must be specfied.
fgen takes multiple files on its command line which means specifications can be placed in several files and then processed together. All files are first parsed and then code generated which means if an attribute is redefined in a later file the definition of the later file is used. If a the file is not local then the full path to the file must be specified.
fgen pays no attention to file extensions so you can organize files however you want. Suggested file extensions are:
- .sm - state machine specification files
% perl D:\main\sw\tools\fgen\fgen.pl ImageDownload.sm
In the example the ++ state machine specified in ImageDownload.sm is generated. Perl is assumed to be in the path. ImageDownload.sm is assumed to be local.
fgen's syntax is very simple, it consists of:
- Blank line separated blocks. Lines must be separated by lines contaning new-lines only. Even spaces on the "blank" line can confuse the parser.
- Where each block consists of one or more new-line separated lines.
- Where each line consists of attributes and their values.
- Where attributes are separated by spaces.
- Blocks begin with a block type key word and a block name. The rest of lines in the block describe the system.
All system specifications follow this basic syntax. Each system must come up with a way to map the system to this syntax. Attributes can be anything and values can be anything so it's not really difficult. Go here to see an example.
(file) ::= (specs)* (specs) ::= (block) | (cfg) | (sm) | (msg) (block) ::= (block_start) (block_entry)+ (blank_line) (block_start) ::= (block_type) (ws) (block_name) (nl) (block_type) ::= (id) // a keyword identifying the block type (block_name) ::= (id) // name of the block (block_entry) ::= (block_line) (nl) (block_line) ::= whatever grammar required for the block type (dir_path) ::= "(path)" | <(path)> (inc_list) ::= INC (ws) dir_path [, (ws) dir_path]* (path) ::= (id) // a relative or full path to a directory (value) ::= (id) (id) ::= a string with no embedded spaces (nl) ::= a carriage return (blank_line) ::= a blank line (ws) ::= white space (number) ::= a string of digits (lighteraff) ::= any series of characters (boolean) ::= 0 | 1
The CFG block allows the specification of general attributes used in the generation of code. For example, the attribute DEBUG_LEVEL specified the default debug level to use when generating debug statements. You may have a lot of configuration attributes are almost none.
(cfg) ::= (cfg_start) (cfg_entry)+ (blank_line) (cfg_start) ::= CFG (ws) (id) (nl) (cfg_entry) ::= (cfg_attr) (cfg_value) (nl) (cfg_attr) ::= (id) // ID from the configurable attributes list (cfg_value) ::= whatever the value should be for the attribute
Any attribute not in the configurable attribute list is automatically added even though it's not "official." This allows new parsers to have their own configurable attributes. All configurable attributes are in the associative array $::CFG{$attribute}. A small example:
CFG Remote Peer State Machine DEBUG 1 DRIVERS lngen.pl INC "Project/LnTypes.h" IGNORE_SAME_ENTRY_EXIT_TRANSITION 0
A state machine is made up of SM block and one or more IN blocks. The SM block defines the state machine in general and the IN blocks define the events, state transitions, and the actions to take.
The state machine block defines attributes about the state machine. It is like a CFG block for the state machine.
(sm) ::= (sm_start) (start_state) (sm_entry)+ (blank_line) (in)+ (sm_start) ::= SM (ws) (sm_name) (nl) (sm_name) ::= (id) // name of the state machine (sm_entry) ::= (case_err) | (inc_list) | (timer) | (is_protect) | (state_type) | (sm_module) | (method) | (doc) | (on_doc) | (on_args) | (do_args) | (do_doc) | (attr) | (debug) | (derive) | (gen_as_string) | (gen_inject_event) | (gen_notify_call) | (gen_eforwarder) (start_state) ::= START (ws) (id) (nl) (case_err) ::= CASE_ERR (ws) (lighteraff) (nl) (is_protect) ::= IS_PROTECT (ws) (boolean) (nl) (state_type) ::= STATE_TYPE (ws) (id) (nl) (sm_module) ::= IS_MODULE (ws) (boolean) (nl) (doc) ::= DOC (ws) (text) (nl) (on_doc) ::= ON_DOC (text) (nl) (do_doc) ::= DO_DOC (ws) (action) (ws) (text) (nl) (on_args) ::= ON_ARGS (ws) (event_name) (ws) ARGS (ws) (args) (nl) (do_args) ::= DO_ARGS (ws) (action_name) (ws) ARGS (ws) (args) (nl) (method) ::= METHOD (ws) (var_type) , (method_body) (nl) (debug) ::= DEBUG_LEVEL (ws) (level) (attr) ::= ATTRIBUTE (ws) (var_type) , (var_name), (var_value) (nl) (var_type) ::= A type designation for a method attribute. (var_name) ::= Name of the attribute. (var_value) ::= Value for an attribute. (text) ::= A string of any length. The \ character can be used to break a string across lines. (level) ::= A debug level approriate to the code generator. (event_name) ::= The name of an event. (action_name) ::= The name of an action. (derive) ::= DERIVE (ws) (dclasses) (dclasses) ::= The classes to derive the state machine from. It's a standard C++ derivation specification (public X). (gen_as_string) ::= GEN_AS_STRING (ws) (0|1) (gen_inject_event) ::= GEN_INJECT_EVENT (ws) (0|1) (gen_notify_call) ::= GEN_NOTIFY_CALL (ws) (0|1) (gen_eforwarder) ::= GEN_EFORWARDER (ws) (0|1) (args) ::= (var_type) (var_name) [, (var_type) (var_name)]
A small example is:
SM RemotePeerSm START IDLE METHOD virtual int, gt() ATTRIBUTE int, mVal, 0 ON_ARGS HealthCheckOk ARGS int x, int y DO_ARGS RepairOm ARGS int x, int y DOC This class remotely peers with a peer. \ More text of great use.
The IN block defines what happens when an event is generate when the state machine is in a given state. A state machine can have any number of IN blocks. You'll have one IN block for every event that transitions the state machine.
(in) ::= (in_start) (which_sm)* (which_in)* (timer)* (transition)+ [(on_entry)] [(on_exit)] (blank_line) (which_sm) ::= SM (ws) (which_state) [, (ws) (which_state)]* (nl) (which_state) ::= (state_name) | CURRENT | GLOBAL (which_in) ::= IN [(doc)] | (in_list) (doc) ::= DOC text (in_list) ::= (ws) (state_name) [, (ws) (state_name)]* (nl) (in_start) ::= IN (ws) (state_name) [, (state_name]+ (nl) (transition) ::= [NEXT | FNEXT] (ws) (state_name) (ws) ON (event) (ws) [(onerr) (ws)] [(if) (ws)] [(do) (ws)] (nl) (do) ::= DO (ws) (action) (ws) [(timer)][, (do)] | DO (ws) (action) | (timer_stop) [, (do)] (if) ::= IF (ws) (test) (onerr) ::= ONERR (ws) PREVSTATE | (state_name) | (invoke) (timer_stop) ::= TIMER_STOP (ws) (timer_name) (timer) ::= TIMED_BY (ws) (timer_name) PERIOD (dmsecs)[(imsecs)] [ACTION TRUE|FALSE] (key) ::= identifier of one of the predefined configurable values (dmsecs) ::= number of delay milli-seconds | (method_name()). Delay is the delay before the timer first fires. (imsecs) ::= number of interval milli-seconds | (method_name()) (method_name) ::= name of a method to call to the attribute value. (test) ::= a method returning an integer (timer_name) ::= (id) // name of a timer (action) ::= (id) // name of an action (event) ::= (id) // name of an event (state_name) ::= (id) // name of a state (invoke) ::= (id)() // function/method/macro name with parens (on_entry) ::= ON_ENTRY (do) (on_exit) ::= ON_EXIT (do)
A small example:
IN IDLE DOC This state means we are rich. ON_ENTRY DO TIMER_STOP HealthTimer ON_EXIT DO RepairOm TIMED_BY HealthTimer PERIOD 3.4 NEXT KNOWN ON PeerStateMsgRcvd \ DO NewState TIMED_BY MsgTimer PERIOD GetNormalMsgToVal() NEXT FAILED ON PeerFailed \ DO TIMER_STOP MsgTimer, \ DO FailedState IN FAILED DOC This state means we have lost all our money. NEXT IDLE ON PeerNotFailed NEXT KNOWN ON PeerStateMsgRcvd IF IsPeerNotFailed() \
Lines beginning with # are comments.
Lines span more than one line by putting a </I> at the end of the line.
The CFG block allows the specification of general attributes used in the generation of code. For example, the attribute DEBUG_LEVEL specified the default debug level to use when generating debug statements. You may have a lot of configuration attributes are almost none.
The BNF is in section CFG BNF.
A small example:
CFG Remote Peer State Machine DEBUG 1 DRIVERS lngen.pl INC "Project/LnTypes.h"
Attribute | Default | Meaning |
DEBUG | 0 | Set the debug level:
|
CCEXT | cpp | C++ extension to use for C++ source files. |
TIMER_CLASS | Timer | Name of the timer class to use when generating timers. |
TIMER_INC | "Osencap/LnTTimer.h" | Path to use in the include statement to include the timer class. |
TIMER_IS_LNOBJECT | 0 | Controls if code is generated to use Destroy() to delete the timer. It should be used when timers are dirived from LnObjec.t |
TIMER_FUNC | virtual int HandleTimer() | Method invoked on timer callbacks. |
TIMER_SET | Start | Method of the timer object to start a timer. This method must
accept the argument:
|
TIMER_CANCEL | Cancel | Method of the timer object that cancels a timer. |
MUTEX_CLASS | LnMutex | Name of the mutex class. |
MUTEX_INC | "Osencap/LnMutex.h" | Path to use in the include statement to include the mutex class. |
ERR_TYPE | LnStatus | The type of the error return. |
NO_ERR | LN_OK | Value use to indicate no error occurred. |
MSG_CLASS | Msg | Name of the message class to use. |
INC | none | Automically includes the specified include file in every generated
class definition. For example:
INC "Util/Log.h" |
SEARCH_PATH | fgen's directory | SEARCH_PATH is comma separated list of paths of where to
so search drivers, specified with the DRIVERS
attribute. By default, the directory where fgen is located
is added to the search path. For example:
SEARCH_PATH /h/bin, /h2/someplace |
DRIVERS | none | DRIVERS is a comma separated list of paths to drivers.
Each driver is expected to generate appropriate code based
on the specification. Paths should be full paths unless the module
will be found in the perl library search path (the @INC variable).
For example:
DRIVERS /h/fgen.pl, yourfgen.pl |
OVERWRITE | 1 | OVERWRITE controls if generated files overwrite
existing files. Options:
|
GEN_ASSERT | 0 | GEN_ASSERT tells the code generator to generate assertions.
By default code is generally generated without asserts.
|
GEN_DEBUG | 1 | GEN_DEBUG tells the code generator to generate debug statements
where it thinks they would make sense.
|
DEBUG_LEVEL level | Output::LEVEL_L2 |
The default debug level to use when creating debug statements.
It can be any valid level.
|
IGNORE_SAME_ENTRY_EXIT_TRANSITION | 0 | Decides if ON_ENTRY and ON_EXIT behaviours are called when transition
to the same state. Applications may have reasons for not wanting
these behaviours called on same state transitions.
|
See an example .
The easiest way to understand how the state machine specification works is to take a look at an example.
A state machine is made up of SM block and one or more IN blocks. The SM block defines the state machine in general and the IN blocks define the events, state transitions, and the actions to take.
A small example is:
CFG Remote Peer State Machine DEBUG 1 DRIVERS lngen.pl INC "Project/LnTypes.h" SM RemotePeerSm START IDLE METHOD virtual int, GetNormalMsgToVal() ATTRIBUTE int, mVal, 0 ON_ARGS HealthCheckOk ARGS int x, int y DO_ARGS RepairOm ARGS int x, int y DOC This class remotely peers with a peer. \ More text of great use. ON_DOC PeerStateMsgRcvd This event means a message was received. DO_DOC NewState This action does something. IN IDLE DOC This state means we are rich. \ Let me tell you what i do all day. ON_ENTRY DO TIMER_STOP HealthTimer ON_EXIT DO RepairOm TIMED_BY HealthTimer PERIOD 3.4 NEXT KNOWN ON PeerStateMsgRcvd \ DO NewState TIMED_BY MsgTimer PERIOD GetNormalMsgToVal() NEXT FAILED ON PeerFailed \ DO TIMER_STOP MsgTimer, \ DO FailedState IN FAILED DOC This state means we have lost all our money. NEXT IDLE ON PeerNotFailed
For the more technically minded state machine's are defined by BNF .
A state machine consists of:
- list of states that can be entered
- events causing movement from one state to another
- actions performed when a state transition occurs
fgen allows the above to be encoded simply and also provides some value-add with the addition of timers. Operations can be timed. Timer objects are automatically generated and managed.
A state machine is defined by two blocks:
- SM defines the overall state machine. There is one SM block per state machine.
- IN defines each state in the state machine. There is one IN block per state in the state machine.
Multiple state machines can be defined in the same file. By default states are associated with the current state machine which means the state machine definition (SM block) they are lexically below in the file.
Using the SM attribute of an IN block which defines state a state may be associate with one or more state machines.
This example shows a fairly complicated state machine for maintaining the state of a remote peer entity. Notice how the state machine uses timers. The timers are automatically generated and managed. All the code implementing the state machine is generated. All you have to do is specify the state machine.
To use the state machine you just derive from it, implement the actions, and call the event methods when they occur. This tool can save a considerable amount of effort, especially when a state machine is changing a lot. Agents and state machines combined together are especially powerful. An agent can define its state machine using fgen and then inherit from it. Messages arriving to an agent are events that move the state machine through its states. The actions are implemented by the agent to do the agent's work.
# NAME: # RemotePeer.sm - state machine for Remote Peer stste # # SYNOPSIS: # fgen.pl RemotePeer.sm # # DESCRIPTION: # This file describes an example state machine for maintaining the # state of a remote peer entity. # # Running fgen on this file will create the C++ class implementing # the state machine. # # SEE ALSO: fgen # # TYPE: SPECIFICATION ######################################################################### CFG Remote Peer State Machine DEBUG 1 DRIVERS lngen.pl INC "Project/LnTypes.h" SM RemotePeerSm START IDLE METHOD virtual int, gt() METHOD virtual bool, IsPeerFailed() METHOD virtual bool, IsPeerNotFailed() METHOD virtual int, GetNormalMsgToVal() ATTRIBUTE int, mVal, 0 ON_ARGS HealthCheckOk ARGS int x, int y DO_ARGS RepairOm ARGS int x, int y IS_MODULE 1 IN IDLE ON_ENTRY DO TIMER_STOP HealthTimer ON_EXIT DO RepairOm TIMED_BY HealthTimer PERIOD 3.4 NEXT KNOWN ON PeerStateMsgRcvd \ DO NewState TIMED_BY MsgTimer PERIOD GetNormalMsgToVal() NEXT FAILED ON PeerFailed \ DO TIMER_STOP MsgTimer, \ DO FailedState NEXT FAILED ON Entry IF IsPeerFailed() DO FailedState NEXT IDLE ON Entry \ DO UnknownState TIMED_BY MsgTimer PERIOD GetNormalMsgToVal() NEXT OVERDUE ON MsgTimerFire \ DO Nothing TIMED_BY MsgTimer PERIOD getOverdueMsgToVal() IN FAILED NEXT IDLE ON PeerNotFailed NEXT KNOWN ON PeerStateMsgRcvd IF IsPeerNotFailed() \ DO NewState TIMED_BY MsgTimer PERIOD GetNormalMsgToVal() IN KNOWN NEXT KNOWN ON PeerStateMsgRcvd \ DO CheckForNewState TIMED_BY MsgTimer PERIOD GetNormalMsgToVal() NEXT FAILED ON Entry IF IsPeerFailed() DO FailedState NEXT IDLE ON Entry \ DO UnknownState TIMED_BY MsgTimer PERIOD GetNormalMsgToVal() NEXT FAILED ON PeerFailed \ DO TIMER_STOP MsgTimer, \ DO FailedState NEXT OVERDUE ON MsgTimerFire \ DO Nothing TIMED_BY MsgTimer PERIOD getOverdueMsgToVal() IN OVERDUE NEXT KNOWN ON PeerStateMsgRcvd \ DO CheckForNewState TIMED_BY MsgTimer PERIOD GetNormalMsgToVal() NEXT FAILED ON PeerFailed \ DO TIMER_STOP MsgTimer, \ DO FailedState NEXT FAILED ON MsgTimerFire \ DO FailedState
The following files are produced:
- A state machine header file. The filename is based on the value of attribute SM in the SM block start.
- A state machine source file. The filename is based on the value of attribute SM. The extension is defined by attribute CCEXT.
A state machine block starts when the keyword SM is seen at the beginning of a line. State machine blocks describe attributes about a state machine. Other blocks describe each state in the state machine.
Attribute | Meaning |
SM name | This is block start for the SM block. name is the
name of the state machine. The generated source files are named
after SM. Attributes for SM are defined in their
own table .
|
Attribute | Default | Meaning |
START state | required |
Initial state of the machine. A required field for state machines. For example: SM Test START IDLE |
INC inc_list | NA |
A list of files to include in the statema machine header.
Usually files are included to resolve any user defined types.
|
IS_PROTECT 1 | 0 | 0, no Mutex |
If IS_PROTECT is set to 1 then all state machine
transitions are protected by a mutex. The default is no
mutex protection.
|
ATTRIBUTE var_type, var_name, var_value | 0 |
ATTRIBUTE specifies a variable to make part of the state machine class.
For each attribute in a state machine there will be a corresponding
attribute in the state machine class definition. The variable will have
type var_type and variable name var_name. The variable
will be initialized in the constructor to value var_value.
This allows the state machine to be better separated from its derived
class.
|
ON_ARGS event_name ARGS (args) |
ON_ARGS allows an event to be called with arguments. The arguments will
be passed to any actions defined as taking parameters (see DO_ARGS).
This allows, for example, a message to be passed to an action directly
without the message having to be stored in a object variable.
|
|
DO_ARGS action_name ARGS (args) |
DO_ARGS allows an action to be called with arguments. Arguments will be
passed from an event to the action. The variable names have to be the
same in the event and in the action.
|
|
STATE_TYPE id | State |
STATE_TYPE allows you to replace a state machine's automatically
generated state enumeration with your own preexisting state type.
You must make sure the enum labels in your state machine match
the labels in your enum. An INC statement must be used to
to include a file that will resolve the enum type.
|
TIMED_BY | NA |
A timer may be defined that is not used explicitly by any
transition. Code for the timer is generated. Such timers must be
manually managed by the programmer using the start and stop timer
methods. Please see TIMED_BY
for more details on the the syntax.
|
IS_MODULE boolean | 0, not a module | A state machine can be made part of a module when the IS_MODULE keyword is set to 1. When set the state machines constructor will take a module pointer as an argument. Debug macros are passed the module pointer. |
METHOD type, body | NA |
User defined methods can be easily added into the state machine
and used as timer value accessors or if tests. The method may be
virtual or completely inline.
type is the return type of the method.
|
DOC text | NA |
Documentation for the state machine class. It is included in a comment
block. text is a string of any length. The \ character can be used
to break comments across lines.
For example:
This is broken on another line. |
ON_DOC event text | NA |
Documentation for an event. It is included in a comment block
for the event. event is the name of an event in the state machine.
text is a string of any length. The \ character can be used
to break comments across lines.
For example:
This is broken on another line.
ON_DOC Event2 Some example documentation. |
DO_DOC action text | NA |
Documentation for an action used an a DO clause. It is included in a
comment block for the action. action is the name of the action
used in a DO clause. text is a string of any length. The \
character can be used to break comments across lines.
For example:
This is broken on another line.
ON_DOC Do2 Some example documentation. |
DEBUG_LEVEL level | Output::LEVEL_L2 |
The debug level to use when creating debug statements for the state machine.
It can be any valid level. It defaults to the level specified in CFG.
|
CASE_ERR code | D(XMOD, XLVL, \"UNHANDLED: EVENT=event STATE=\" << CurrentState() <<\"\\n\");\n\t\trc= LN_FAIL;\n"; |
Code fragment to use in the default case of a state switch for
an event. The string event in the fragment is replaced with
the current event name. The string XMOD is replace with the name
of the module. The string XLVL is replaced with the current debug level.
|
DERIVE (ws) (dclasses) | None. |
This directive specifies classes the state machine should derive from.
It allows state machines to have base classes.
|
GEN_AS_STRING (ws) (0|1) | 0 |
Causes the method CurrentStateName to be generated. This method
returns the current state as a string value. The value 0 means
don't generate the method. This is the default. The value 1
means generate the method.
|
GEN_EFORWARDER (ws) (0|1) | 0 |
Causes the pure virtual method FwdEvent to be generated.
It also causes GEN_INJECT_EVENT to be defined. The InjectEvent
method instead of calling events directly, creates an Action object that
when its Doit method is invoked, causes the correct state machine event
method to be invoked. The Action object is passed to FwdEvent for
forwading to a thread context that will invoke the Doit method in its
thread context. The Doit method calls the correct event method and
thus causes state changes in the state machine. State machine actions
invoked because of the event will be executed in the thread context.
|
GEN_NOTIFY_CALL (ws) (0|1) | 0 |
Causes the pure virtual method SmChangedEvent to be generated.
This method is called by the state machine whenever the state machine
changes state. Derived class are exected to implement this method
and do whatever is appropriate when the state changes.
|
GEN_INJECT_EVENT (ws) (0|1) | 0 |
Causes the method InjectEvent to be generated.
This method can be used to trigger an event using only the event
name. Normally an event is triggered by calling the method
corresponding to the event. InjectEvent will take the event name
and invoke the correct event method. No arguments can be passed
to events when this option is used.
|
See an example SM block.
Each state is defined by a state block which includes:
- the name of the state
- one line each for each state the current state can transition to
- a blank line for separating state blocks
Attribute | Meaning |
IN state_name [, state_name]+ |
This is the block start for the state definition.
state_name is the name of the state. An IN statement
may have multiple state names which means the transitions will be
executed if any of the specified states are the current state.
|
Attribute | Meaning |
SM which_sm |
SM specifies which state machines a state is associated with.
which_sm is a comma separated list of state machines names
for which the state definition should be apart.
or SM TestSm, CURRENT or SM GLOBAL or do not specify one |
IN which_in |
IN specifies the global state definitions to merge into this one.
which_in is a comma separated list of state names in the global
state machine that are to be merged into this one. See attribute SM
in this table for more information.
|
NEXT state_name | Specifies the next state to transition to when the specified event occurs. NEXT is defined in its own table . |
TIMED_BY |
A timer may be defined that is not used explicitly by any
transition. Code for the timer is generated. Such timers must be
manually managed by the programmer using the start and stop timer
methods. Please see TIMED_BY
for more details on the the syntax.
|
Attribute | Meaning | |
NEXT state_name |
Specifies the next state to transition to when the specified event
occurs. The next state must exist in the state machine which means it
must have its own definition.
|
|
FNEXT state_name |
FNEXT stands for forced next and is the same as NEXT except
the state is ALWAYS set to state_name. FNEXT is used when an
IF clause is present as an IF clause makes the state transition
conditional. Sometimes we want
the state to always change but have the action only be executed when
the IF condition is true.
|
|
ON event |
Specifies the event that must occur to cause the state transition. A method
is created in the state machine class named directly after event. When
the event occurs the event's method must be called to trigger the state
transitions. The state machine can't know when events occur, code using the
the state machine must interpret events.
or NEXT INFO ON YourTimerNameFire |
|
ONERR PREVSTATE | (state_name) | (invoke) |
ONERR specifies what should happen when an action called during
a state transition fails. As there is no one prefered approach
to handling action failures several options are provided. Remember,
a state machine's state has been set to target state of a
transition before any actions are called. So by default
when an action returns an error the state transition exits
immediately remaining in the current state.
| |
IF test |
The IF clause places a conditional on the state transition.
The test method is implemented by a class.
test is called before state transition happens.
If test returns 0 then the transition will
not happen. Any non-zero value will cause the transition
to occur.
or NEXT INFO ON YourTimerNameFire IF IsStable |
|
DO action |
The action to perform on the state transition. More than one DO clause
can be specified by comma separating them. A pure virtual function is
defined for each action in the state machine. Classes are expected to
derive from the state machine class and implement the actions. See the
generated header files for the actions to implement. Actions are called
as events arrive and trigger state transitions.
| |
ON_ENTRY do |
ON_ENTRY allows actions to be performed every time a state is entered.
Actions are specified using the DO clause. Actions are performed
before transition actions are performed.
| |
ON_EXIT do |
ON_EXIT allows actions to be performed every time a state is exited.
Actions are specified using the DO clause. Actions are performed
before transition actions are performed.
| |
TIMED_BY timer_name PERIOD (dmsecs)[.(imsecs)] |
Creates a timer to time the action specified in the DO clause.
Timers not created in a state transition context should ignore the
DO clause explanations.
| |
DOC text | NA |
Documentation for the state. It is included in a comment block for
the state. text is a string of any length. The \ character
can be used to break comments across lines.
For example:
This is broken on another line. |
From a state machine specification a state machine header file and implementation file are generated. The header file is divided in to sections.
- Timer Classes
A class is generated for each timer specified. - State Machine Class
Next is the state machine class. In the state machine class:- State Enum
An enum for every state in your state machine. - Event Methods
A method for every event driving your state machine. Events are generated from the ON clauses in your state machine. - Action Methods
A pure virtual function for every action in your state machine. Actions are generated from the DO clauses in your state machine. Events cause action methods to be called. - Timer Methods
A start and stop method is generated for every timer. This allows you to stop and start the timer in your state machine actions.
- State Enum
State machines are driven by events. As events come into your system, from messages, interrupts, or some other source, you must invoke the corresponding event method in the state machine object. Events are coded to call the action methods you specified and change states appropriately.
The action methods are virtual. You must derive a class from the state machine class and implement the action methods. Its in the action methods that the behaviour of the state machine is coded.
- The error handling could be improved. A lot errors just don't generate good code.
- *.sm - state machine specification file