rFSM is a small and powerful Statechart implementation. It is mainly designed for Coordination of complex systems but is not limited to that. rFSM is written in pure Lua and is therefore highly portable and embeddable. As a Lua domain specific language rFSM inherits the extensibility of its host language.
Table of Contents
- Install
- Introduction
- Specifying rFSM models
- Executing rFSM models
- Common pitfalls
- Extensions
- Tools
- More examples, tips and tricks
- API Summary
- Footnotes
- Acknowledgement
Just run make install to install for Lua 5.1, 5.2, 5.3, 5.4 and 5.5.
Alternatively, add the src/ directory of this repo to your Lua module
path (e.g. LUA_PATH="/path/to/rFSM/src/?.lua;/path/to/rFSM/src/?/init.lua;$LUA_PATH").
The only runtime dependency is the (pure Lua)
uutils module (which itself has
no dependencies). The colorized pretty-printer (rfsm.pp)
additionally uses ansicolors, and the test-suite uses
luaunit.
To run the test-suite:
make test # or: cd tests && ./run.luarFSM is a minimal Statechart flavor designed for the Coordination of complex systems such as robots. It has the following features:
- Hierarchical (composite) states
- Completion events
- Parametrizable and reusable states
- Easy to build statemachines by composing existing states/state machines
- Plugin mechanism permits extending the core engine. Available plugins include timeevents, event memory, sequential AND states and more.
- Real-time safe operation possible using lua-tlsf/rtp [1]
The following shows a simple hello_world example
1 return rfsm.state {
2 hello = rfsm.state { exit=function() print("hello") end },
3 world = rfsm.state { entry=function() print("world") end },
4
5 rfsm.transition { src='initial', tgt='hello' },
6 rfsm.transition { src='hello', tgt='world', events={ 'e_done' } },
7 rfsm.transition { src='world', tgt='hello', events={ 'e_restart' } },
8 }The first line defines a new toplevel composite state and returns
it. The root state of an rFSM state machine is always a state
itself. This permits it to be composed as a substate in a different
state machine. The return statement facilitates reading rfsm model
files by tools or other state machines.
The second and third line define two leaf states that are part of the
toplevel composite state. hello defines an exit function and
world an entry function which are called when the state is
exited/entered, respectively.
The next three lines define transition between these states. The first
is from the initial connector to the hello state. This transition
will be taken the first time the composite state is entered. The
initial connector, as an exception, need not be defined and will be
created automatically when referenced from a transition.
The next transition is from hello to world and is triggered by the
e_done event. This event is raised internally when a state
completes, which is either the case when the state's doo function
(see below) finishes or immediately, if there is no doo, as is the
case here. The third transition is triggered by the e_restart event.
Next we execute this statemachine in the rfsm-simulator:
$ tools/rfsm-sim examples/hello_world.lua
Lua 5.1.4 Copyright (C) 1994-2008 Lua.org, PUC-Rio
rFSM simulator v0.1, type 'help()' to list available commands
INFO: created undeclared connector root.initial
> step()
hello
active: root.hello(done)
queue: e_done@root.helloWe execute step() to advance the state machine once. As this is the
first step, the fsm is entered via the initial connector to the
hello state. After that the state hello is active and done
(because no doo function is defined). Consequently, an e_done
completion event has been generated and placed in the queue. So the
next step:
> step()
world
active: root.world(done)
queue: e_done@root.world... causes a transition to world. As the world state completion
event does not trigger any transitions, running step() again does
not have any effect:
> step()
active: root.world(done)
queue:But we can manually send in the e_restart event and call step(),
which takes us back to hello:
> se("e_restart")
> step()
hello
active: root.hello(done)
queue: e_done@root.hellorFSM state machines are constructed using three model elements: states, connectors and transitions.
(all functions are part of the rfsm module, thus need to be called in
Lua with the rfsm prefix, e.g. rfsm.state{})
States are used to model discrete states of the system and can be
either composite or leaf states. A composite state contains
other states, while a leaf state does not. States can define entry
and exit functions
entry(fsm, state, 'entry')
exit(fsm, state, 'exit')that are called when the state is entered or exited respectively. The arguments passed in are the toplevel statechart, the current state and the string 'entry' resp. 'exit'. Normally you don't need these arguments and should not change them either. (The rationale behind the second and third argument is to permit one function to handle entry and exit of multiple states and hence needs to identify these).
Leaf states may additionally define a do function (it is called doo
in rFSM to avoid clashes with the identically named Lua keyword).
bool doo(fsm, state, 'doo')The doo function is used to perform actions while a leaf state is active. To that end it can be used such that it is repeatedly called until either the function completes or an event triggers a transition to a different state.
Implementationwise, this function is treated as a Lua coroutine. This enables the following two use-cases:
-
doois a regular function:doois executed once and a completion evente_doneis raised afterwards (if nodoofunction is defined this event is raised immediately after execution of theentryfunction). -
Long running
doowith voluntary preemption: while possible, it is not recommended to define adoofunction that runs for a longer time, because this would prevent incoming events to trigger transitions. Therefore, therfsm.yield()call can be inserted at appropriate points into a long runningdooto explicitly return control to the rfsm engine, that then checks for new events and potentially executes transitions.
(Note: rfsm.yield is currently only an alias to coroutine.yield)
The following example illustrates the second use case:
doo = function(fsm)
while true do
if min_distance() < 0.1 then
rfsm.send_events(fsm, "e_close_obj")
end
rfsm.yield()
end
endThis doo will check a certain condition repeatedly and raise the
e_close_obj event if it is true. Each cycle the control is returned
to the rFSM core by calling rfsm.yield().
rfsm.yield(idle_flag) accepts a Boolean argument (called the "idle
flag") that influences how doo is called by the rFSM core: if true
it will cause the rFSM core to go idle, provided there are no other
events. If false (the default [2] if no
arguments are given) and there are no other events, doo will be
called in a tight loop. It depends on each application which
idle_flag is appropriate. In general the idle_flag should always be
true unless the intention is that the doo function is executed as
fast as possible (potentially consuming a lot of CPU!).
The root composite state honors some extra fields to refine the global FSM behavior.
Configuring error, warning, informational and debug output. The
err, warn, info and dbg fields can be used to fine tune how
these messages are output. The value of these fields can be either
true or false or set to a function that accepts a variable list of
arguments. The default is to write errors and warnings to stderr and
info to stdout. Debug messages are turned off by default. Nicer and
configurable pretty printing of debug output is provided by the
rfsm.pp module
(described below).
Note: the names
err,warn,info,dbgandgeteventsare reserved for these toplevel hooks — do not use them as state or connector names.
The getevents hook. The getevents hook is called by the
rFSM core whenever it needs to check for new events. This function is
the central mechanism to integrate rFSM into existing systems. The
expected behavior is to return a Lua table of events (array part
only). These events are then used to check for enabled transitions.
Transitions define how a state machine changes state upon receiving events:
Example:
rfsm.transition {
src='stateX', tgt='stateY', events = {"e1", "e2"},
guard=function()
if getVal() > 0.3 then
return false
end
return true
end,
effect=function (fsm, tr, 'effect', events) do_this() end
}The above defines a transition between stateX and stateY which is
triggered by the events e1 or e2 (i.e. event lists have OR
semantics — any of the listed events enables the transition). To
trigger only after several events have been received, possibly across
different steps, use the rfsm.await
extension.
The guard condition (optional) will prevent the transition from being
executed if it returns false. The effect function (optional) is
executed while the transition is taken (after exiting the source and
before entering the target). It receives the toplevel fsm, the
transition tr, the string 'effect' and the table of events that
enabled the transition. If no events are specified at all, the
transition is enabled by any event.
As a shorthand, a single event may be given as a bare string instead of a one-element table:
rfsm.trans{ src='a', tgt='b', events='e_go' } -- same as events={'e_go'}An event entry may also be a predicate function. It is called with each received event and enables the transition if it returns a truthy value. This is useful for wildcard matching:
-- trigger on any event whose name starts with 'e_err'
rfsm.trans{ src='a', tgt='error',
events={ function(e) return type(e)=='string' and e:match('^e_err') end } }Three ways of specifying the src and target states are
supported: local, relative or absolute. In the above example
stateX and stateY are referenced locally and must therefore be
defined within the same composite state as the transition.
Relative references specify states that are more deeply nested (relative to the position of the transition). Such references starts with a leading dot. For example:
return rfsm.state{
operational=rfsm.state{
motors_on = rfsm.state{
moving = rfsm.state{},
stopped = rfsm.state{},
rfsm.trans{src='initial', tgt='stopped'},
},
rfsm.trans{src='initial', tgt='motors_on'},
},
off=rfsm.state{},
rfsm.trans{src='initial', tgt=".operational.motors_on.moving" },
rfsm.trans{src=".operational.motors_on.stopped", tgt='off', events={'e_off'} }
}The first transition is defined between the (locally referenced)
initial connector to the relatively referenced moving state. This
permits to refine the default behavior of the operational state,
namely entering motors_on.stopped (due to the initial connectors),
to instead enter the motors_on.moving state.
The second transition defines a transition from the relatively
referenced operational.motors_on.stopped to off. Here the
intention is to constrain the states from which one can reach the
off state: turning the device off is only permitted if it is not
moving.
At last, absolute references begin with root.. Using absolute syntax
is strongly discouraged for anything other than testing, as it breaks
compositionality: if a state machine is used within a larger state
machine the absolute reference is broken.
Furthermore, transitions support so called priority
numbers. Priority numbers serve to resolve conflicts within one
hierarchical level. In case two transitions are enabled by a set of
events, the transition with the higher priority number will be
executed. Priority numbers are defined with the pn keyword on
transitions, as shown below. Transitions without priority numbers are
assumed to have priority 0.
rfsm.trans{ src='following', tgt='hitting', pn=10, events={ 't6' } },If possible, statecharts should be designed not to depend on priority numbers and introduce these rather as an optimization.
Connectors permit to define so called compound transitions by chaining multiple transition segments together. Connectors are similar to the UML junction element. Compound transitions are statically evaluated, meaning that the compound transition is only executed if each subtransition is enabled (events match and guards are true).
Also see the examples connector_simple.lua and
connector_split.lua. The latter uses a
connector (rendered as a choice diamond) to dispatch to different
error states based on the received event:
Connectors are useful for defining interfaces (entry and exit points) that hide internals of a composite state. The following example defines a error handling state:
return rfsm.state {
software_err = rfsm.state{},
hardware_err = rfsm.state{},
initial = rfsm.conn{},
recovered = rfsm.conn{},
failed = rfsm.conn{},
rfsm.trans{src='initial', tgt='software_err', events={'e_sw_err'}},
rfsm.trans{src='initial', tgt='hardware_err', events={'e_hw_err'}},
rfsm.trans{src='software_err', tgt='recovered', events={'e_recovered'}},
rfsm.trans{src='hardware_err', tgt='recovered', events={'e_recovered'}},
rfsm.trans{src='software_err', tgt='failed', events={'e_failed'}},
rfsm.trans{src='hardware_err', tgt='failed', events={'e_failed'}},
}Transitions 1 and 2 dispatch to different error handling states based on the events received. Transitions 3, 4, 5 and 6 connect the states to different exit connectors based on the events they generate.
Note: defining cycles is possible, but dangerous, unsupported and discouraged. It may make the yogurt in your fridge grow fine grey beards.
Before running, a state machine must be initialized. This serves to
validate the fsm model and transform the fsm to be suitable for
execution. Initialization is done using the rfsm.init(fsm) function,
that takes a (string) rfsm description as input and returns an
initialized fsm. To load an rfsm from a file and initialize it, the
rfsm.load(filename) function can be used:
fsm = rfsm.init(rfsm.load("fsm.lua"))If the return value from rfsm.init is not false, initialization
succeeded and the returned fsm can be run.
The function rfsm.step(fsm, n) will attempt to step the given fsm
for a maximum of n times. A step can be either the execution of a
transition or a single execution of the doo program. step will
return either when the state machine is idle or the given number
of steps has been reached. The boolean return value indicates whether
the fsm is idle (true) or the maximum amount of requested steps was
reached (false).
For each step the rfsm engine will invoke the getevents hook to
retrieve new events and then reason about what to do (which
transitions to execute or doo's to run). After that these events are
discarded. If this seems inconvenient, checkout the event memory
extension.
When omitted, the number of steps argument n to rfsm.step defaults
to 1.
rfsm.run(fsm) calls step as long as the given fsm is not idle. Not
idle means: there are either events in the queue or there is an active
doo function that is not idle.
To directly send events to the fsm the function rfsm.send_events(fsm, e1, e2, ...) can be used. The first argument is the fsm to which all
subsequent event arguments are sent to.
-
Name clashes between state/connector names with reserved Lua keywords.
This can be worked around by using the following syntax:
['end'] = rfsm.state{...}- Executing functions accidentially
It is a common mistake to execute externally defined functions instead of adding references to them:
stateX = rfsm.state{ entry = my_func() }The (likely) mistake above is to execute my_func and assigning the
result to entry instead of assigning my_func:
stateX = rfsm.state{ entry = my_func }Of course the first example would be perfectly valid if my_func()
returned a function as a result!
-
Why doesn't my statemachine react if I send a completion event
e_donefrom the outside?Short answer: because it is a syntactic shortcut for the completion event of the source state of the transition which it is defined on. During initalization it is transformed to
e_done@fqn(e.g.e_root@root.stateA.stateB) If you send in the expanded completion event it will work.Explanation: a completion event only makes sense in the context of a state which completed. Making the state which has completed explicit in the event avoids accidentially triggering a transition labeled with a higher priority completion event that has nothing to do with the current one.
The same holds true for
rfsm.timeeventbased timeevents. -
My FSM is using up 100% CPU, what's wrong?
Most likely you have defined a long running
doofunction that does not callrfsm.yieldwith atrueargument (the idle flag). Therefore the rFSM engine calls thedoofunction in a tight loop. -
My FSM is doing nothing, my guard are not executed, … , although I'm running
steporrunperiodically!rFSM will only attempt to transition if it has at least one event in the queue! If you only want to transition based on guards, raise a dummy event (e.g. "e_any").
Extensions are Lua modules that are required alongside your model to
add new event syntax, transition conditions, or debug hooks to
the rFSM engine. They integrate via the rfsm.preproc plugin mechanism
and have no effect unless explicitly loaded.
This module extends the rFSM engine with time events. Time events are
automatically raised after the specified time after entering a state
has elapsed. To enable time events, it suffices to load the
rfsm.timeevent module. Both relative (e_after) and absolute
(e_at) timeevents are supported. These can be specified on transitions
using the e_after(duration) / e_at(abstime) syntax, as shown in the
following example:
local timeevent = require("rfsm.timeevent")
rfsm.trans{ src='A', tgt='B', events={ timeevent.e_after(0.1) } },The timeevent will be raised 100ms after state A was entered.
Alternatively, instead of a numeric timeout, a function to retrieve the timeout can be passed:
rfsm.trans{ src='A', tgt='B', events={ timeevent.e_after(function() return 3 end) } Optionally, an arbitrary id can be passed to the e_after
function. This will be used as a unique id in the raised event name
and also passed to the gettimeout function. This is useful for
using a single gettimeout function to retrieve different timeouts from
a table.
local timeouts = {
TIMEOUT1 = 3,
TIMEOUT2 = 10,
}
local function gettimeout(id) return timeouts[id] end
...
rfsm.trans{ src='A', tgt='B', events={ timeevent.e_after(gettimeout, 'TIMEOUT1') },
rfsm.trans{ src='B', tgt='A', events={ timeevent.e_after(gettimeout, 'TIMEOUT2') },e_at(abstime) works the same way but fires once the clock reaches the
given absolute time (in seconds, in the same epoch as the configured
gettime hook) rather than relative to state entry. Like e_after, it
also accepts a function and an optional id.
The only requirement to use rfsm.timeevent is that a gettime
function is configured using the rfsm.timeevent.set_gettime_hook(f)
function. f is expected to return the current time as a single
value in nanoseconds.
Examples can be found in
examples/timeevent.lua.
Warning: these timeevents only work while the rfsm engine is running and can not magically wake up an idle fsm. Therefore this type of timeevents typically only makes sense for fsm that are "stepped" at a fixed frequency or that never go idle.
Note: the old
"e_after(1.5)"string syntax is still supported, but deprecated.
This extension adds memory of events that occurred to an rFSM
statechart. This is done maintaining a table emem for every
state. The keys in this table are event names and the values the
number of times that event occurred while the respective state was
active. The emem table is cleared when a state is exited by setting
all values to 0.
This extension is useful for defining transitions that are taken only after certain events have occurred, but that do not necessarily occur within one step. Because the rFSM engine drops events after each steps this information would otherwise be lost.
To enable event memory, all you need to do is load the rfsm.emem
module. Checkout the examples/emem_test.lua for more details.
In a nutshell, this plugin permits to trigger transitions only after multiple events have been received. These events can be received in different steps.
This is basically a specialized version of the emem plugin. This one should be preferred if no counting is required, since it is computationally much less expensive.
Behavior: When loaded, the plugin scans for events with the syntax
await(event1, event2). This statement is transformed as follows:
-
a guard condition is generated and added to possibly existing guard conditions. It will only enable the transition if the both events have been received while the source state is active.
-
a second hook is installed in the exit function of the source state to reset the event counting. So when the source state is exited (either via or not via the await transition) and reentered again, the counting start from the beginning. It would be trivial to provide a variant of await that resets the counts only if the await transition is taken, however it is not clear right now if that would be useful at all.
For more information checkout the await.lua example.
The pp.gen_dbgcolor function generates a configurable and colorful
dbg hook.
Usage:
pp.gen_dbgcolor(name, dbgids, defshow)nameis the (optional) string name to print prefixing the debug outputdbgidsis a table that enables or disables certain dbg ids by setting them to true or false. Known debug ids are:STATE_ENTER,STATE_EXIT,EFFECT,DOO,EXEC_PATH,ERROR,HIBERNATING,RAISED,TIMEEVENTdefshow(bool) defines whether debug id's not mentioned in the dbgids table are shown or not.
Example:
local pp = require("rfsm.pp")
local fsm = rfsm.init(...)
fsm.dbg = pp.gen_dbgcolor("fsm1", { STATE_ENTER=true, STATE_EXIT=true}, false)Will show only STATE_ENTER and STATE_EXIT debug messages.
This debugging helper plugin will at load-time construct a list of all events used in the FSM. If at runtime an event is received which is not known in the known list, a warning message will be printed.
To use, just require the module before creating your fsm. Important: load it after other plugins that transform events (such as timeevents), so that it picks up the transformed events.
Command-line programs for working with rFSM models from the outside.
A small command line simulator for running a fsm interactively. It
drops into an interactive Lua prompt; type help() to list the
available commands (step, se, run, pp, …).
$ tools/rfsm-sim examples/hello_world.luaConverts an rFSM model to JSON format. Based on the rfsm.rfsm2json
module; requires lua-json.
$ tools/rfsm2json examples/hello_world.luaRenders rFSM models as PlantUML state diagrams.
It writes a .puml file next to each input model:
$ tools/rfsm2plantuml examples/composite_nested.luaThe output can be rendered with the plantuml command line tool or any
PlantUML server. The underlying rfsm.plantuml Lua module can also be
used directly from code — it is pure Lua with no external dependencies:
local plantuml = require("rfsm.plantuml")
local fsm = rfsm.init(rfsm.load("mymodel.lua"))
print(plantuml.encode(fsm, "my model")) -- return diagram as a string
plantuml.save(fsm, "mymodel.puml") -- ... or write it to a fileThe examples/ directory contains many small models. A
gallery of rendered state diagrams is available in
doc/img/examples/.
The graphical model:
and the corresponding textual representation:
-- any rFSM is always contained in a state
return rfsm.state {
dbg = true, -- enable debugging
on = rfsm.state {
entry = function () print("disabling brakes") end,
exit = function () print("enabling brakes") end,
moving = rfsm.state {
entry=function () print("starting to move") end,
exit=function () print("stopping") end,
},
waiting = rfsm.state {},
-- define some transitions
rfsm.trans{ src='initial', tgt='waiting' },
rfsm.trans{ src='waiting', tgt='moving', events={ 'e_start' } },
rfsm.trans{ src='moving', tgt='waiting', events={ 'e_stop' } },
},
error = rfsm.state {
doo = function (fsm)
print ("Error detected - trying to fix")
rfsm.yield()
math.randomseed( os.time() )
rfsm.yield()
if math.random(0,100) < 40 then
print("unable to fix, raising e_fatal_error")
rfsm.send_events(fsm, "e_fatal_error")
else
print("repair succeeded!")
rfsm.send_events(fsm, "e_error_fixed")
end
end,
},
fatal_error = rfsm.state {},
rfsm.trans{ src='initial', tgt='on',
effect=function() print("initializing system") end },
rfsm.trans{ src='on', tgt='error', events={ 'e_error' } },
rfsm.trans{ src='error', tgt='on', events={ 'e_error_fixed' } },
rfsm.trans{ src='error', tgt='fatal_error', events={ 'e_fatal_error' } },
rfsm.trans{ src='fatal_error', tgt='initial', events={ 'e_reset' } },
}This is easy! Let's assume the state machine is is a file "subfsm.lua"
and uses the strongly recommended return rfsm.state ... syntax, it
can be included as follows:
return rfsm.state {
name_of_state = rfsm.load("subfsm.lua"),
otherstateX = rfsm.state{},
...
}Make sure not to forget the ',' after the rfsm.load() statement!
The LuaCookbook page describes how to do this.
Functions to define rFSM:
| Function | Short aliases | Description |
|---|---|---|
state{} |
sista{}, csta{} |
create a state |
connector{} |
conn{} |
create a connector |
transition{} |
trans{} |
create a transition |
(sista and csta are historical aliases for simple and composite
state; both are identical to state — whether a state is composite or
a leaf is derived from whether it contains substates.)
| Function | Description |
|---|---|
fsm rfsm.init(fsmmodel) |
create an initialized rfsm instance from model |
fsm rfsm.load(file) |
load an (uninitialized) model from a file |
fsm rfsm.load_str(str[,name]) |
load an (uninitialized) model from a string |
idle rfsm.step(fsm, n) |
attempt to transition FSM n times. Default: once |
rfsm.run(fsm) |
run FSM until it goes idle |
rfsm.send_events(fsm, ...) |
send one or more events to internal rfsm event queue |
rfsm.reset(fsm) |
reset to the inactive state (re-enters on next step) |
fqn rfsm.get_actleaf_fqn(fsm) |
fqn of the active leaf state (or nil) |
tab rfsm.get_active_conf(fsm) |
list of active state fqns from root down to the leaf |
The following hook functions can be defined for a toplevel composite state and allow to refine various behavior of the state machine.
| Function | Description |
|---|---|
dbg |
called to output debug information. Set to false to disable. Default: false. |
info |
called to output informational messages. Set to false to disable. Default: stdout. |
warn |
called to output warnings. Set to false to disable. Default stderr. |
err |
called to output errors. Set to false to disable. Default stderr. |
table getevents() |
function which returns a table of new events which have occurred. |
Lower level functions (not for normal use):
Use these to manage step hooks. Setting pre_step_hook and
post_step_hook directly is not permitted anymore:
| Function | Description |
|---|---|
pre_step_hook_add(fsm, hook, where) |
install function hook to be called before each rfsm step of fsm |
post_step_hook_add(fsm, hook, where) |
install function hook to be called after each rfsm step of fsm |
pre_step_hook_rm(fsm, hook) |
remove a previously added pre step hook (returns true if removed) |
post_step_hook_rm(fsm, hook) |
remove a previously added post step hook (returns true if removed) |
idle_hook(fsm): if defined, called instead of returning from
step/run functions. Used only for debugging purposes.
timeevents: thegettimehook must return the time in nanoseconds.- transition
effectfunctions now receive the triggeringeventstable as their fourth argument:effect(fsm, tr, 'effect', events). - the Lua modules now live under
src/rfsm/; therequirenames are unchanged (rfsm,rfsm.pp,rfsm.timeevent, …).
New, backwards-compatible additions:
- String / predicate events:
eventsmay be given as a bare string (events='e_foo') or contain predicate functions for wildcard matching — see Transitions. - Absolute time events:
rfsm.timeevent.e_at(abstime)fires once the clock reachesabstime— seerfsm.timeevent. rfsm.load_str(str): build a model from a string instead of a file, e.g.rfsm.init(rfsm.load_str("return rfsm.state{}"))— see Operational functions.rfsm.get_active_conf(fsm): return the active state configuration as a list of fqns — see Operational functions.rfsm.pre_step_hook_rm/rfsm.post_step_hook_rm: remove a previously installed step hook — see Hooks.rfsm.plantuml: a pure-Lua PlantUML state-diagram exporter — seerfsm.plantuml.
[1] See this Real-time Linux Workshop paper, lua-tlsf and the minimal Lua real-time POSIX bindings.
[2] The reason for this choice of default is that it fails more obviously (100% CPU load) than the opposite (doo function not executed properly).
-
Funding
The research leading to these results has received funding from the European Community's Seventh Framework Programme (FP7/2007-2013) under grant agreement no. FP7-ICT-231940-BRICS (Best Practice in Robotics)
-
Scientific background
This work borrows many ideas from the Statecharts by David Harel and some from UML 2.1 State Machines. The following publications are the most relevant
David Harel and Amnon Naamad. 1996. The STATEMATE semantics of statecharts. ACM Trans. Softw. Eng. Methodol. 5, 4 (October 1996), 293-333. DOI=10.1145/235321.235322 http://doi.acm.org/10.1145/235321.235322
The OMG UML Specification: http://www.omg.org/spec/UML/2.3/Superstructure/PDF/



