[Qt-interest] Qt State machine framework
Sean Harmer
sean.harmer at maps-technology.com
Thu Mar 25 10:41:39 CET 2010
Hi,
On Thursday 25 March 2010 05:32:02 Mandeep Sandhu wrote:
> Hi Frank,
>
> First of all, thanks for taking the time to write such an elaborate
> mail and explaining your point of view.
>
> The reason my questions might look out of place is because I have not
> given the complete context of my problem (though I've explained it in
> different thread on this list).
>
> I'm writing an FSM to emulate the behaviour of a h/w. The h/w vendor
> provides lib's to access the underlying h/w which has a propensity to
> go into undesirable states if the right command is not given to it.
> And the lib also does not guard us from such erroneous behaviour. (I
> can't discuss details of the h/w for the usual reasons. Hope you
> understand)
>
> So after experimenting a lot with the h/w we finally have an idea of
> what works an what doesn't.
>
> Based on this, I've designed my FSM. So the FSM acts as a layer of
> protection in that if an event/command is illegal in a particular
> state, then it's never issued to the h/w. The FSM tells me, when in a
> particular state, what all events/transitions are possible.
>
> Initially I thought of implementing the SM as a "flat" FSM, but when I
> read the Qt state machine framework, they talked about using HSMs
> (heard of it the first time then!). Then as you and No'am pointed out
> I read about HSMs and tried to fit my SM in that model. I ended up
> with a pretty neat SM, with considerably less number of
> states/transitions (compared to the flat SM approach).
>
> I'm implementing my own "psuedo-driver" for this h/w as a plugin. This
> driver maintains the SM and talks to the h/w using the vendor's lib.
> Application developers use this plugin to talk to the h/w.
>
> Now coming back to the need for knowing the previous state.
>
> This is mostly for an application developer. The SM itself has no need
> to know the previous state.
>
> I think it'll be helpful for the app developer to know where the SM
> transitioned from, especially since we can can land up in the same
> state from 2 different source states (obviously on 2 diff events at
> different times). Some of these events are generated internally (from
> an app's point of view). Just knowing the current state might not
> suffice. OR it might, if we assume that it's the apps responsibility
> to keep track of state transitions (I just tell it whenever a new
> transition happens and the new state).
>
> Also, I believe that concepts like history states/ state groups etc
> are internal to the SM (since it's used only as a way to express the
> SM). From the outside, even if I transition to state group (using
> history or default state) I still should be able to find out what
> child state I'm in, since that really represents the true state. The
> caller is totally ignorant of the state group concept.
>
> Similarly I need to know what's the _current_ state the SM is in. So
> if I've transitioned back to to a history state, I'm interested in
> knowing what child state it really represents. I've yet to try the API
> No'am suggested, so can't say for sure whether that'll return me the
> child state the SM is in or the state group/history state.
>
> Hope that explains my requirements and need to go for a SM.
If this is purely for use by an application developer, then you might be
interested in a simple trick that we use when developing HSMs with the Qt
framework. What we do is to make all of our states inherit from a custom class
called State as follows:
class State : public QState
{
Q_OBJECT
public:
explicit State( const QString& name, QState* parent = 0 );
QString name() const { return m_name; }
public slots:
void setName( const QString& name ) { m_name = name; }
protected:
virtual void onEntry( QEvent* e )
{
Q_UNUSED( e );
// Print out the state we are entering and it's parents
QString state = m_name;
State* parent = dynamic_cast<State*>( parentState() );
while ( parent != 0 )
{
state = parent->name() + "->" + state;
parent = dynamic_cast<State*>( parent->parentState() );
}
qDebug() << "Entering state:" << state;
}
virtual void onExit( QEvent* e );
{
Q_UNUSED( e );
// Print out the state we are exiting and it's parents
QString state = m_name;
State* parent = dynamic_cast<State*>( parentState() );
while ( parent != 0 )
{
state = parent->name() + "->" + state;
parent = dynamic_cast<State*>( parent->parentState() );
}
qDebug() << "Exiting state:" << state;
}
protected:
QString m_name;
};
With this, every time your SM transitions from one state to another you will
see qDebug() messages for the state it is leaving and the state it is entering
and all of their parent states.
We only do this for developer builds and turn it off for other builds by only
conditionally inheriting from this class using #ifdef's.
As Noam suggested you can use history states to return to a previous state and
these can indeed be deep so that they go into the deepest child state that you
were in previously in the target state hierarchy. We use this to recover from
hardware errors or user input errors (e.g. parameter out of range supported by
the hardware) exactly as you intend to do. Coupled with the above State class
we get back to where we were and from the debug output we can see from where
we came.
If you need to depend upon knowing where you came from at runtime for some
other purpose then you will need to use the method suggested by Noam and use
the triggered() signal of QAbstractTransition.
Good luck the HSM concept is very powerful but it can take a little while and
practice to get your head around it.
Cheers,
Sean
More information about the Qt-interest-old
mailing list