Creating state charts with Astade

Creating the state chart

In Astade a state chart is a class. So you can add state charts in any “classes” folder in any package. Here you see our “CTest” state chart for this tutorial.

Drawing the state chart

Now you can add states to your state chart and add transitions. From time to time you can use the context menu of the state chart to let Astade draw the corresponding state diagram.

Lets start with an simple example. Assume we need this state chart:

You can see, we have 3 states in this state chart (tick, tack and toe). “Tick” is the initial state. Further we have 4 actions in this state chart (prepare, shout, yell and sleep). “Prepare” is done, when the state chart is initialized. “Shout” is done, when we change from “tick” to “tack” and when we go from “toe” to “tick”.

There are four events (draw, push, reverse and goon) which cause the state changes.

Edit the state chart

You can edit this state chart by adding states to the state chart and transitions to the states. At the end it looks like this:

Coding the state chart

Finally you can ask Astade to generate code for the state chart. The code depends heavily on the type of message passing system you use. Astade supports 7 different coders for state charts:

  • StateChartCoder is a general coder for C++. It is independent from the message passing system but a little “unhandy” to use.
  • StateChartCoderACF codes state charts in C for the message passing system “Astade C Framework”
  • StateChartCoderACF++ codes state charts in C++ for the message passing system “Astade C Framework”
  • StateChartCoderC is a general coder for C.
  • StateChartCoderCdSys is a special coder that codes state charts for a message passing system used at dSys
  • StateChartCoderVSF is a special coder that codes state charts for a message passing system used at Rea
  • StateChartCoderWx is a special coder that codes state charts for a message passing system used at wxWidgets

You can configure the coder Astade shall use in the state chart feature dialog. Select “ACF++” if you want Astade to use the “StateChartCoderACF++”, for example.

Then you will get this code:

Specification

The coder generates two header files. “CTest.h” specifies an abstract class for the state chart. You must inherit from this class and implement the actions, to finish.

The second file (ACF_evnts.h) is a header file you can include in all your classes that may send events to the state chart.

CTest.h
/******************************************************************
 * This code was generated by the Astade StateChartCoderACF++.    *
 * The generated code is not per se a derivative work of Astade;  *
 * the original copyright to this code is held by the holder(s)   *
 * of the copyright to the underlying model at the time this code *
 * was generated; this code may be distributed under terms of     *
 * his/her/their choice.                                          */
/** A test state chart
 *  @file CTest.h
 ******************************************************************/
 
#ifndef __CTest_h
#  define __CTest_h
 
// specification prolog
// include the framework
#include "ACF.h"
#include "ACF_state_machine_base.h"
 
/** @dot
digraph G {
	fontsize=10
	labelloc = "t"
	labeljust = "l"
	label = "CTest\nA test state chart"
	NOSTATE [shape=circle label="" width=0.2 style=filled fillcolor=black color=black];
	NOSTATE->tick [label="prepare", color=black, fontname=arial, fontsize=10,  arrowhead=vee];
	toe [label="{toe}", shape=Mrecord, color=darkviolet, fontname=arial, fontsize=12];
	toe->tick [label="goon / sleep; shout", color=black, fontname=arial, fontsize=10,  arrowhead=vee];
	toe->tack [label="reverse / ", color=black, fontname=arial, fontsize=10,  arrowhead=vee];
	tack [label="{tack}", shape=Mrecord, color=darkviolet, fontname=arial, fontsize=12];
	tack->toe [label="push / yell", color=black, fontname=arial, fontsize=10,  arrowhead=vee];
	tick [label="{tick}", shape=Mrecord, color=darkviolet, fontname=arial, fontsize=12];
	tick->tack [label="draw / shout", color=black, fontname=arial, fontsize=10,  arrowhead=vee];
}
@enddot
 
A test state chart
*/
class CTest : public ACF_state_machine_base
{
public:
	//! @brief Constructor.
	CTest(struct ACF* aACF);
 
	//! @brief Destructor.
	virtual ~CTest();
 
	//! @brief Call this function once to initialize the state machine.
	//! Calling this function a second time will have no effect!
	//! This will call all initial actions and enter state "tick".
	//! The actions are called with the event passed to this function.
	//! @param theEvent The event passed to the initial actions.
	void Initialize(ACF_Message* theEvent);
 
	//! @brief Call this function to pass an event to the state machine.
	//! All events for this state machine must be derived from the event base class: "ACF_Message"
	//! Calling this function is allowed only after calling the "Initialize" function.
	//! @param me Pointer to the ACF_MessageReceiver.
	//! @param theEvent The event to be processed.
	static void TakeEvent(void* me, ACF_Message* theEvent);
 
	//! Helper function to call the state function from the static function
	//! @param theEvent The event to be processed.
	inline void TakeTheEvent(ACF_Message* theEvent) { (this->*theState)(theEvent); }
 
	//! @brief This checks if the machine is in state "toe".
	//! @return is in state.
	inline bool IsIntoe() { return theState == &CTest::toe; }
 
	//! @brief This checks if the machine is in state "tack".
	//! @return is in state.
	inline bool IsIntack() { return theState == &CTest::tack; }
 
	//! @brief This checks if the machine is in state "tick".
	//! @return is in state.
	inline bool IsIntick() { return theState == &CTest::tick; }
 
 
protected:
	//************* actions **************************
	virtual void prepare(ACF_Message* theEvent) = 0;
	virtual void shout(ACF_Message* theEvent) = 0;
	virtual void sleep(ACF_Message* theEvent) = 0;
	virtual void yell(ACF_Message* theEvent) = 0;
	//************* guards **************************
 
	//! You might overload this to visualize the actual state.
	virtual void notifyNewState(const char* state){};
 
private:
	//! @brief This is the default state before the state machine is initialized.
	//! It does nothing. This makes sure that there is no crash if "TakeEvent" is called accidentally before "Initialize".
	//! @param theEvent The event to be processed.
	bool NoState(ACF_Message* theEvent);
 
	//! @brief The pointer holding the current state.
	void (CTest::*theState)(ACF_Message*);
 
	//! @brief The function pointer to the enter function of the next state.
	void (CTest::*nextState)(ACF_Message*);
 
	//! @brief This function calls the current enter Function until a stable state is reached.
	//! @param theEvent The event passed to the actions and guards.
	void EnterState(ACF_Message* theEvent);
 
	/** The "toe" state is importent and explained in this description. As this is only an example, I don't know what I shall write here.
	*/
	void toe(ACF_Message* theEvent);
 
	//! @brief This is the enter function for state toe.
	void Enter_toe(ACF_Message* theEvent);
 
 
	/** The "tack" state is importent and explained in this description. As this is only an example, I don't know what I shall write here.
	*/
	void tack(ACF_Message* theEvent);
 
	//! @brief This is the enter function for state tack.
	void Enter_tack(ACF_Message* theEvent);
 
 
	/** The "tick" state is importent and explained in this description. As this is only an example, I don't know what I shall write here.
	*/
	void tick(ACF_Message* theEvent);
 
	//! @brief This is the enter function for state tick.
	void Enter_tick(ACF_Message* theEvent);
 
};
 
 
// specification epilog
 
#endif // #ifdef __CTest_h
ACF_events.h
/******************************************************************
 * This code was generated by the Astade StateChartCoderACF++.    *
 * The generated code is not per se a derivative work of Astade;  *
 * the original copyright to this code is held by the holder(s)   *
 * of the copyright to the underlying model at the time this code *
 * was generated; this code may be distributed under terms of     *
 * his/her/their choice.                                          */
/** A test state chart
 *  @file ACF_events.h
 ******************************************************************/
 
extern const char* draw;
extern const char* goon;
extern const char* push;
extern const char* reverse;

Implementation

The coder generates two code files. “CTest.cpp” implements the code for the statechart class. “ACF_events.c” implements the event objects.

CTest.cpp
/******************************************************************
 * This code was generated by the Astade StateChartCoderACF++.    *
 * The generated code is not per se a derivative work of Astade;  *
 * the original copyright to this code is held by the holder(s)   *
 * of the copyright to the underlying model at the time this code *
 * was generated; this code may be distributed under terms of     *
 * his/her/their choice.                                          */
/** A test state chart
 *  @file CTest.cpp
 ******************************************************************/
 
// implementation prolog
#include "CTest.h"
#include "ACF_events.h"
 
CTest::CTest(struct ACF* aACF)
{
	#ifdef _TRACE_
	ACF_Trace ACF_LOCALTRACEHELPER;
	ACF_Trace_notify_constructor(&ACF_LOCALTRACEHELPER,&MessageReceiver_base, 5, "CTest");
	#endif
	// Call the message framework constructor
	#ifdef _TRACE_
	ACF_MessageReceiver_Constructor(&MessageReceiver_base, "CTest", &CTest::TakeEvent, this, aACF);
	#else
	ACF_MessageReceiver_Constructor(&MessageReceiver_base, 0, &CTest::TakeEvent, this, aACF);
	#endif
	#ifdef _TRACE_
	ACF_Trace_notifyReturn(&ACF_LOCALTRACEHELPER);
	#endif
}
 
CTest::~CTest()
{
	#ifdef _TRACE_
	ACF_Trace ACF_LOCALTRACEHELPER;
	ACF_Trace_notify_destructor(&ACF_LOCALTRACEHELPER,&MessageReceiver_base, 5, "CTest");
	#endif
	ACF_MessageReceiver_Destructor(&MessageReceiver_base);
	#ifdef _TRACE_
	ACF_Trace_notifyReturn(&ACF_LOCALTRACEHELPER);
	#endif
}
 
void CTest::Initialize(ACF_Message* theEvent)
{
	#ifdef _TRACE_
	ACF_Trace ACF_LOCALTRACEHELPER;
	ACF_Trace_notify_function_call(&ACF_LOCALTRACEHELPER,&MessageReceiver_base, 5, "CTest", "Initialize");
	#endif
	// Calling the initial actions
	prepare(theEvent);
	// Set the initial State function
	nextState = &CTest::Enter_tick;
	// Call the state enter function
	EnterState(theEvent);
	#ifdef _TRACE_
	ACF_Trace_notifyReturn(&ACF_LOCALTRACEHELPER);
	#endif
}
 
void CTest::TakeEvent(void* me, ACF_Message* theEvent)
{
	#ifdef _TRACE_
	ACF_Trace ACF_LOCALTRACEHELPER;
	ACF_Trace_notify_self_call(&ACF_LOCALTRACEHELPER, me, 5, "CTest", "TakeEvent");
	#endif
	CTest* pthis = (CTest*)((ACF_MessageReceiver*)me)->this_ptr;
	pthis->TakeTheEvent(theEvent);
	// Call the state enter function
	pthis->EnterState(theEvent);
	#ifdef _TRACE_
	ACF_Trace_notifyReturn(&ACF_LOCALTRACEHELPER);
	#endif
}
 
bool CTest::NoState(ACF_Message* theEvent)
{
	return false;
}
 
void CTest::EnterState(ACF_Message* theEvent)
{
	while (nextState)
		(this->*nextState)(theEvent);
}
 
void CTest::toe(ACF_Message* theEvent)
{
	// goon / sleep; shout ---> tick
	if (theEvent->ID == goon)
	{
		// next state
		nextState = &CTest::Enter_tick;
		// Actions
		sleep(theEvent);
		shout(theEvent);
		return;
	}
	else
	// reverse /  ---> tack
	if (theEvent->ID == reverse)
	{
		// next state
		nextState = &CTest::Enter_tack;
		return;
	}
	else
	return;
}
 
void CTest::Enter_toe(ACF_Message* theEvent)
{
	// Set the new state.
	theState = &CTest::toe;
	// call the virtual notify function
	notifyNewState("toe");
 
	// maybe trace the state entering
	#ifdef _TRACE_
	ACF_Trace_notify_state(&MessageReceiver_base, 5, "CTest", "toe");
	#endif
	nextState = 0; // We stay in this state
}
 
void CTest::tack(ACF_Message* theEvent)
{
	// push / yell ---> toe
	if (theEvent->ID == push)
	{
		// next state
		nextState = &CTest::Enter_toe;
		// Actions
		yell(theEvent);
		return;
	}
	else
	return;
}
 
void CTest::Enter_tack(ACF_Message* theEvent)
{
	// Set the new state.
	theState = &CTest::tack;
	// call the virtual notify function
	notifyNewState("tack");
 
	// maybe trace the state entering
	#ifdef _TRACE_
	ACF_Trace_notify_state(&MessageReceiver_base, 5, "CTest", "tack");
	#endif
	nextState = 0; // We stay in this state
}
 
void CTest::tick(ACF_Message* theEvent)
{
	// draw / shout ---> tack
	if (theEvent->ID == draw)
	{
		// next state
		nextState = &CTest::Enter_tack;
		// Actions
		shout(theEvent);
		return;
	}
	else
	return;
}
 
void CTest::Enter_tick(ACF_Message* theEvent)
{
	// Set the new state.
	theState = &CTest::tick;
	// call the virtual notify function
	notifyNewState("tick");
 
	// maybe trace the state entering
	#ifdef _TRACE_
	ACF_Trace_notify_state(&MessageReceiver_base, 5, "CTest", "tick");
	#endif
	nextState = 0; // We stay in this state
}
 
 
// implementation epilog
ACF_events.c
/******************************************************************
 * This code was generated by the Astade StateChartCoderACF++.    *
 * The generated code is not per se a derivative work of Astade;  *
 * the original copyright to this code is held by the holder(s)   *
 * of the copyright to the underlying model at the time this code *
 * was generated; this code may be distributed under terms of     *
 * his/her/their choice.                                          */
/** A test state chart
 *  @file ACF_events.c
 ******************************************************************/
 
const char* draw = "draw";
const char* goon = "goon";
const char* push = "push";
const char* reverse = "reverse";
tutorial/statecharts.txt · Last modified: 2015/05/13 16:48 by thomas
GNU Free Documentation License 1.3
Powered by PHP Driven by DokuWiki Recent changes RSS feed Valid CSS Valid XHTML 1.0 Valid HTML5