Discussion:
[MSM] a timer/timeout for the whole state machine (including submachines)
Albert Gil Moreno
2012-04-04 10:20:46 UTC
Permalink
Hi,

I've been working for a while with the MSM and I'm very impressed about
it... congratulations for your work!!

I've to say that I'm not an expert on UML nor state machines and I think
that my needs are a very common user-case... so I'm probably missing
something!

Basically what I need is a (reusable) Timer that, after a number of Clocks,
it sends a TimeOut to the state machine, and when the Timer receives a
Reset it restarts the counting of clocks.
(I want to also add a Pause or similar but this doesn't matter right now...)

So, the goal is that any (or all) of my states could be controlled by the
Timer and if no events are received for a while a TimeOut will be (always)
received/processed.

AFAIK there is no concept of timer/timeout in the MSM, but I found very
straightforward to implement my idea with an Orthogonal Region with a
single state Timer that receives Clock and Reset events and sends a TimeOut.
And it worked like a charm!!

But I found a problem with one of my states become a submachine...
The Timer::Reset action executes a fsm.process_event(Timer::Reset()), but
this event is not processed.
I realized that "fsm" is the submachine and AFAIK there is no way to access
to the parent(est) fsm...

So, is there a better or more standard way to implement a Timer/Timeout in
UML/MSM?
And, although "fsm" is the submachine, why the no_transition function is
not executed (this last seems a bug to me...).


Albert

PD: Do you think that has meaning that the MSM could have an "on_timeout"
function for the states (like on_entry/exit?), or to have a timeout event
like "none"... ok may be this sounds huge to implement and not so generic...
Christophe Henry
2012-04-04 20:30:07 UTC
Permalink
Hi,
I've been working for a while with the MSM and I'm very impressed about it... congratulations for your work!!
Thanks :)
I've to say that I'm not an expert on UML nor state machines and I think that my needs are a very common user-case... so I'm probably >missing something!
Basically what I need is a (reusable) Timer that, after a number of Clocks, it sends a TimeOut to the state machine, and when the Timer
receives a Reset it restarts the counting of clocks.
(I want to also add a Pause or similar but this doesn't matter right now...)
Yes, you could also use a region with 1 state and internal transitions but it's about the same.
So, the goal is that any (or all) of my states could be controlled by the Timer and if no events are received for a while a TimeOut will
be (always) received/processed.
AFAIK there is no concept of timer/timeout in the MSM, but I found very straightforward to implement my idea with an Orthogonal
Region with a single state Timer that receives Clock and Reset events and sends a TimeOut.
And it worked like a charm!!
But I found a problem with one of my states become a submachine...
The Timer::Reset action executes a fsm.process_event(Timer::Reset()), but this event is not processed.
I realized that "fsm" is the submachine and AFAIK there is no way to access to the parent(est) fsm...
Yes it's something which has been requested a few times, it will be done, but I didn't come to it yet. In the meantime you have 2 solutions:
- exit the submachine using an exit pseudo state, it's UML conform and visible in your diagram.
- keep a pointer to the parent-est machine. Not very beautiful but it'll work.
So, is there a better or more standard way to implement a Timer/Timeout in UML/MSM?
There is no timer in MSM for 2 reasons:
- it's out of the scope of the library and I'm probably not the ideal person for this (too system-dependent).
- it would have to be done in another thread generating tick events => possible race condition.

The only solution is outside the fsm. You can use asio, which will cost you a thread and you'll have to prevent the race condition. You can use a posix function to this aim too. To my knowledge, there is no other timer in boost (I looked but I might have missed it).
And, although "fsm" is the submachine, why the no_transition function is not executed (this last seems a bug to me...).
I don't think so. If the submachine doesn't handle the event, the upper one could, so it gets a chance to do it, etc. until the top-level fsm. If no fsm including the top-level fsm brings a no_transition, then it's a bug.
Albert
PD: Do you think that has meaning that the MSM could have an "on_timeout" function for the states (like on_entry/exit?), or to have a
timeout event like "none"... ok may be this sounds huge to implement and not so generic...
And system-dependent. I have the hope someone in boost will provide this one day. Then I'd be happy to use it in MSM.

HTH,
Christophe
Albert Gil Moreno
2012-04-05 09:25:08 UTC
Permalink
Thanks for your fast reply!

Yes, you could also use a region with 1 state and internal transitions but
Post by Christophe Henry
it's about the same.
"As much as possible in transition table" is one idea I got from you!
;-)
Post by Christophe Henry
I realized that "fsm" is the submachine and AFAIK there is no way to
access to the parent(est) fsm...
Yes it's something which has been requested a few times, it will be done,
- exit the submachine using an exit pseudo state, it's UML conform and
visible in your diagram.
- keep a pointer to the parent-est machine. Not very beautiful but it'll work.
I've already tryied the ugly pointer solution... and works.
I'm gonna try the first one, also to try an exit pseudo state... i've never
used yet!
Post by Christophe Henry
- it's out of the scope of the library
just this reason seems enought to me!
;-)
Post by Christophe Henry
And, although "fsm" is the submachine, why the no_transition function is
not executed (this last seems a bug to me...).
I don't think so. If the submachine doesn't handle the event, the upper
one could, so it gets a chance to do it, etc. until the top-level fsm. If
no fsm including the top-level fsm brings a no_transition, then it's a bug.
Then... I think we've found a bug?
I'm attaching a version of your tutorial as a test case of this bug.

The bug can be reproduced by:
- Creating a functor action (FA) that process an event (E)
- Creating an state machine (SM) and a submachine (SSM)
- SM and SSM should call FA in some place of their transition table
- E should not be present in the SSM (it can or not be in SM).

If the FA is called by the SM, everything works as expected:
- no_transitions if E is not in SM
- the correct transtion if E is in SM (E is not in SSM)

But if the FA is called bu the SSM, nothing happens:
- no_transition is not called if E is not in SM
- transition is not executed is E is in SM
- ok, I've just checked when E is in an orthogonal region of SM...


Please, let me known if it is a real bug and if a patch is easy or not (may
be I can help...?).
I think that if we fix this bug, then the ugly pointer solution can be
removed!
;-)


Thanks!


Albert
Christophe Henry
2012-04-08 21:56:14 UTC
Permalink
Post by Christophe Henry
Then... I think we've found a bug?
I'm attaching a version of your tutorial as a test case of this bug.
- Creating a functor action (FA) that process an event (E)
- Creating an state machine (SM) and a submachine (SSM)
- SM and SSM should call FA in some place of their transition table
- E should not be present in the SSM (it can or not be in SM).
- no_transitions if E is not in SM
- the correct transtion if E is in SM (E is not in SSM)
- no_transition is not called if E is not in SM
- transition is not executed is E is in SM
- ok, I've just checked when E is in an orthogonal region of SM...
Please, let me known if it is a real bug and if a patch is easy or not (may be I can help...?).
I think that if we fix this bug, then the ugly pointer solution can be removed!
;-)
Yes, I oversaw the valid case of no_transition needing a call when process_event is called on the submachine object directly. I fixed this in the trunk (rev. 77840) so it should now work.
Note that after your last processing of NextSong, no_transition of the Playing (not player) fsm is (now correctly) called, but you are NOT in ErrorMode because error_found is not known in the submachine. If you want to move to ErrorMode in the outer you will need either the ugly pointer solution, or UML-conform, a pseudo exit.
However, the UML-conform way is going to cost you a bit. You'll need to add a second region to the submachine, with a pseudo-exit if error_found is generated. Then, as in the outer fsm you will be in the first region (not where ErrorMode is), you'll need to reprocess this event to the fsm, so that the second region has a chance to get it (through, for example, an internal transition in Playing).

Using the provided example, I add 2 states in the submachine, PlayingAllOk and PlayingError, the latest being a pseudo exit state, then I add a transition in the submachine:
Row < PlayingAllOk, error_found ,PlayingError >

Finally, to reprocess the event, an internal transition (though one could argue, it is not UML conform, replace this by a transition to Playing or any state of the first region if you prefer:

Row < Playing::exit_pt<Playing_::PlayingError> , error_found , none , ActionErrorFound , none >

Now, the second region will get it and you are in ErrorMode. But whether it is less ugly than a pointer is a matter of taste ;-)

HTH,
Christophe
Albert Gil Moreno
2012-04-09 12:13:01 UTC
Permalink
Thanks a lot Christophe!


I've tried the pseudo-exit solution you've proposed and it worked as
you explain, thanks!
But, I'm still a bit confused...
Post by Christophe Henry
If the submachine doesn't handle the event, the upper one could, so it gets a chance to do it, etc. until the top-level fsm. If no fsm including the top-level fsm brings a no_transition, then it's a bug.
If I'm not wrong, now the no_transition is executed by the submachine
(the 77840 fix), but the upper state machine still doesn't have the
change to handle the event.
Is that right?

I'm not sure if this is the expected behaviour or not...

In your first answer I've understood that the "upper-forwarding of
events" was the correct behaviour (UML-conforming?), but after your
Post by Christophe Henry
If you want to move to ErrorMode in the outer you will need either the ugly pointer
solution, or UML-conform, a pseudo exit.
I've tried the pseudo-exit scheme in my Timer/TimeOut, but just to
"emulate" the upper-forwarding of my TimerReset event.
(I do not want to exit from my submachine, just an event to be
upper-forwarded to the parent state machine)
But I get an unexpected exception...
I've returned to te ugly-pointer solution because in fact it seems to
me a closer implementation to the upper-forwarding idea...

But, in fact, I don't really need "access" to the parent state-machine
(nor the ugly-pointer ;-), but just that the events generated in the
submachine's actions (but not present the submachine's table) to be
sent/upper-forwarding to the outer state machine.
Is this the expected behaviour or not?


Thanks a lot for your (fast and great) work!


Albert
Nguyen, Tai
2012-04-09 13:11:58 UTC
Permalink
All,

I am using boost for the first time. I am seeing something that I don't understand. I hope someone can explain this to me.

I have a basic sender/receiver setup.

The sender send a message similar to the following format.

Header: contain size of the message
Body: contain the payload;

The receiver reads the Header into a tempbuffer. Then check the size, and read more into the tempbuffer with an offset of sizeof( Header ).

This works fine for TCP socket. When I switch to use UDP socket, weird behaviours happen.

Read the header into the tempbuffer. However, when I try to read the body, I also get the header again. So the temp buffer turns out to look like this.

Header
Header
Body.

Its seems as if the first read doesn't get the Header off the network stack. Does someone have an explaination for this?

Some background, pseudo code:
1) start a thread for the io_service
2) set up socket;
3) socket->async_recieve_from( buffer, sizeof(header ) ), handle_read_header )
4) handle_read_header()
PayloadSize = header.size;
Socket->asynch_receive_from( buffer[ sizeof( Header )], payloadSize ), handle_read_body );


If I don't give it an index offset on the second read, when finish reading the payload, the tempbuffer also contain the correct header. This seems as if the Header was never clear off the network stack after the first read.

Thanks,
tai
Chris Cleeland
2012-04-09 13:42:40 UTC
Permalink
Tai,
Post by Nguyen, Tai
The sender send a message similar to the following format.
Header: contain size of the message
Body: contain the payload;
The receiver reads the Header into a tempbuffer. Then check the size, and
read more into the tempbuffer with an offset of sizeof( Header ).
This works fine for TCP socket. When I switch to use UDP socket, weird behaviours happen.
Read the header into the tempbuffer. However, when I try to read the body,
I also get the header again. So the temp buffer turns out to look like this.
Header
Header
Body.
I strongly suggest you read more on *networking*, as this is a very basic
issue having nothing to do with boost.

What you are encountering is the difference between TCP and UDP. TCP is
streaming, whereas UDP is packet-oriented. Thus, when you read from TCP,
you will only get AT MOST as many bytes as you request (you may get fewer).
When you read from UDP, you will get exactly a single complete packet,
though it may be out of sequence offers no guarantee of receipt (it does,
however, offer a guarantee that if you receive it, the data received is
what was sent within that packet).

Finally you will likely get better response to networking questions on
lists focused on TCP/IP networking or bsd sockets. comp.unix.networking
comes to mind, and I'm sure there are others as well.

Good luck,
-cj
--
Chris Cleeland
Christophe Henry
2012-04-09 20:19:51 UTC
Permalink
Hi Albert,
Post by Albert Gil Moreno
I've tried the pseudo-exit solution you've proposed and it worked as
you explain, thanks!
But, I'm still a bit confused...
Post by Christophe Henry
If the submachine doesn't handle the event, the upper one could, so it
gets a chance to do it, etc. until the top-level fsm.
If no fsm including the top-level fsm brings a no_transition, then it's
a bug.
As I tried to explain in my last post, it turns out that I forgot the case
where the event is generated on the submachine object instead of the
top-level one.
Post by Albert Gil Moreno
If I'm not wrong, now the no_transition is executed by the submachine
(the 77840 fix), but the upper state machine still doesn't have the
change to handle the event.
Is that right?
Yes. It is what I wanted to achieve.
Post by Albert Gil Moreno
I'm not sure if this is the expected behaviour or not...
In your first answer I've understood that the "upper-forwarding of
events" was the correct behaviour (UML-conforming?), but after your
There is no upper forwarding, we are talking about error handling and which
entity should be the best to do it. From a user point of view, it seems to
me logical that when I process an event on the top-level fsm, I get a
no_transition handler called on this top-level fsm. I don't want a
submachine to generate some error for an event the top fsm is going to
handle right after (the UML Standard sees the priority of event handling be
given to the "deeper" entity). It would only be a disturbance, right?
Now, if a submachine sends an event to itself (as you are doing in your
added transition of the submachine), I also find it logical that the error
handler be located there, this is why no_transition of the submachine is
called.
That's why for this case, I needed to revise my previous position.
Post by Albert Gil Moreno
Post by Christophe Henry
If you want to move to ErrorMode in the outer you will need either the
ugly pointer
solution, or UML-conform, a pseudo exit.
I've tried the pseudo-exit scheme in my Timer/TimeOut, but just to
"emulate" the upper-forwarding of my TimerReset event.
(I do not want to exit from my submachine, just an event to be
upper-forwarded to the parent state machine)
But I get an unexpected exception...
I would need to see your case to give an answer on this.
Post by Albert Gil Moreno
I've returned to te ugly-pointer solution because in fact it seems to
me a closer implementation to the upper-forwarding idea...
I didn't want to say it explicitly but the UML solution seems pretty much
harder than the pointer, yes. But I wanted to give you the choice with a
solution more in the spirit of UML.
As already said, several people asked for actions to get as parameter not
only the current fsm but also the top-level one. I find it a good idea, I am
just lacking time at the moment :(
Post by Albert Gil Moreno
But, in fact, I don't really need "access" to the parent state-machine
(nor the ugly-pointer ;-), but just that the events generated in the
submachine's actions (but not present the submachine's table) to be
sent/upper-forwarding to the outer state machine.
Is this the expected behaviour or not?
If you want this, the correct way is to send the event to the outer machine.
Either through the ugly pointer or through a peudo exit state.
I think the automatic forwarding would be confusing because, unlike the
Standard, MSM sees a submachine as a full state machine in its own right,
which can be reused in another fsm or as stand-alone. With automatic
forwarding, this would be error prone as your submachine would react very
differently depending on its context.
Again, better would be to give you the choice at which level you want to
send the event. I think this will be done with a variadic-style template
list of all the fsm's in a hierarchy for actions and guards. I'm not
finished with the library yet ;-)
In the meantime, I apologize for forcing on you the ugly pointer.

Cheers,
Christophe
Albert Gil Moreno
2012-04-10 10:57:41 UTC
Permalink
Hi!
Post by Christophe Henry
There is no upper forwarding,
Ok!
Now it is totally clear to me!
;-)
Post by Christophe Henry
we are talking about error handling and which entity should be the best to do it.
Well, in fact in I wasn't thinking about error handling...
I was using an orthogonal region for my Timer state/class, not as an
error handler, but as a "no activity handler"... ;-)

My problem arise when the "activity" is generated/processed in a submachine.

Probably I should rethink my design:
- A Timer inside all my (sub)state machines?
- An independent state machine only for the Timer and (not so ugly)
pointers from/to it in all my (sub) state machines?
- More work on the pseudo exit?

I've to think and try them...
If you are curious, I'm sending a version my Timer (without the
threads and other noise I'm working on)...
Post by Christophe Henry
From a user point of view, it seems to me logical that when I process an event on the top-level fsm, I get a no_transition handler called on this top-level fsm. I don't want a submachine to generate some error for an event the top fsm is going to handle right after (the UML Standard sees the priority of event handling be given to the "deeper" entity). It would only be a disturbance, right?
Now, if a submachine sends an event to itself (as you are doing in your added transition of the submachine), I also find it logical that the error handler be located there, this is why no_transition of the submachine is called.
That's why for this case, I needed to revise my previous position.
Sure you are right, but probably because I'm completely newbie on UML,
I see the "no_transition" handler as a kind of "exception cached",
which is more familiar to me... ;-)

For this reason I could think that a (sub) state machine could handle
some "no_transitions" for some events (some cached cases), but it can
also auto-upper-fwd them to the parent state machine...

But this is totally out of the scope of my Timer goal, and I've to
remember: there is no upper-fwd!!
;-)
Post by Christophe Henry
Post by Albert Gil Moreno
But I get an unexpected exception...
I would need to see your case to give an answer on this.
Don't care!
I should work more on it!
Thanks!
Post by Christophe Henry
As already said, several people asked for actions to get as parameter not only the current fsm but also the top-level one. I find it a good idea, I am just lacking time at the moment :(
I know, it will be great, thanks a lot for your time!!
:-)
Post by Christophe Henry
I think the automatic forwarding would be confusing because, unlike the Standard, MSM sees a submachine as a full state machine in its own right, which can be reused in another fsm or as stand-alone.
Sure you are right again, but...

I mean, I like very much the idea of full state machines and to
reusing them, and states, events and actions, of course!
In fact I'm trying to follow this idea as much as I can!

But I don't think that the automatic forwarding would be "confusing",
but the "natural behaviour" of a state machine (?).
(again, I don't known UML, so I'm probably wrong!!)

The upper-forwarding seems as "natural" to me as when the upper state
machine receives an event that is inner-fwd to the active submachine.

Probably, because I'm not familiar with UML, but I am with exceptions,
I see events like a kind of exceptions (generated somewhere...) to be
cached starting from the nearest active (sub)state machine (nearest
caller) and upper-forwarded until the last/outest state machine (main
function).


In fact, in my case I wasn't thinking on terms of full-machines vs
sub-machines, neither in forwarding...
I was just thinking to implement a reusable functor-action
(Timer::Reset) to be placed in any transition table.

This functor action doesn't know anything about the
(sub)states(machines) and events which produce its execution (totally
reusable), but they just send a specific event with the hope of being
cached for some "friendly" state/orth.region (Timer) running in
parallel somewhere, or (if no one catch them) to be just ignored in a
no_transition function.

But probably this is not the UML-way to design (?).
(again, and only if you are curious, take a look to the attached
implementation...)
Post by Christophe Henry
In the meantime, I apologize for forcing on you the ugly pointer.
Come on!!
With MSM a have just one ugly pointer, but without msm... sure I've a
lot of ugly sm code!! ;-))


Thanks a lot Christophe,


Albert
Christophe Henry
2012-04-11 21:43:35 UTC
Permalink
Hi Albert,
Post by Albert Gil Moreno
Post by Christophe Henry
we are talking about error handling and which entity should be the best to do it.
Well, in fact in I wasn't thinking about error handling...
Sorry, then I got it wrong. I interpreted your first post as a missing
no_transition (which really was missing).
Looks like you wanted something else. Let's see if I can help.
Post by Albert Gil Moreno
I was using an orthogonal region for my Timer state/class, not as an
error handler, but as a "no activity handler"... ;-)
Let me see if I got it right. You want a timer to count from x to 0, and
when timer == 0, generate a timeout event, correct?
Then you want the possibility to reset the timer.
IIUC, you implemented your timer as a second region with a single Timer
state. This looks so far ok to me because the region can process events to
the state machine (for other regions).
Of course, the more complicated your timer gets, the harder it becomes.
Post by Albert Gil Moreno
My problem arise when the "activity" is generated/processed in a submachine.
- A Timer inside all my (sub)state machines?
Sounds like a lot of work ;-)
Post by Albert Gil Moreno
- An independent state machine only for the Timer and (not so ugly)
pointers from/to it in all my (sub) state machines?
This looks like a perfect use case for a submachine. Define it once as an
independent state machine and reuse it in any state machine needing a timer.
So far so good, but you are stuck with how to inform client state machines
of your timer that there is a timeout, right?
Post by Albert Gil Moreno
- More work on the pseudo exit?
I was going to suggest you this.
I see 3 possibilities defining a timer (sub) state machine:
- templatize it on an ugly pointer type of the client outer fsm and call
process_event (timeout) on it. Not going to win a beauty contest ;-)
- make it less ugly by using a boost::function to inform caller. Not great
either.
- use the UML way. The timer is a state machine, with a pseudo exit when a
timeout is detected. This is an encapsulated submodule used by any fsm. Like
a well-behaved submodule, it advertises that timeout events are going to be
fired through the pseudo exit. How it is done inside is not the outer fsm's
business.
What is the outer fsm's business, however, is to decide what to do next.
Usually some error handling and the decision of whether to restart the
timer, stop all, whatever. If restart the timer, no problem, the outer
defines a transition from Timer::PseudoExit -> Timer, so that the timer can
restart.
This has several advantages:
- no ugly pointer
- UML-conform
- perfectly reusable and encapsulated timer for different uses. Often, fsm's
need a timer but differ in how they handle timeouts. Leave it to them. The
documentation states the timeout is sent through a pseudo exit. Or several
pseudo exit states.
- the timer is a state machine in its own right, you can write a wonderful
unit test for it alone.
Post by Albert Gil Moreno
From a user point of view, it seems to me logical that when I process an
event on the top-level fsm, I get a no_transition handler called on this
top-level fsm. I don't want a submachine to generate some error for an
event the top fsm is going to handle right after (the UML Standard sees
the priority of event handling be given to the "deeper" entity). It would
only be a disturbance, right?
Now, if a submachine sends an event to itself (as you are doing in your
added transition of the submachine), I also find it logical that the error
handler be located there, this is why no_transition of the submachine is
called.
That's why for this case, I needed to revise my previous position.
Sure you are right, but probably because I'm completely newbie on UML,
I see the "no_transition" handler as a kind of "exception cached",
which is more familiar to me... ;-)
For this reason I could think that a (sub) state machine could handle
some "no_transitions" for some events (some cached cases), but it can
also auto-upper-fwd them to the parent state machine...
But this is totally out of the scope of my Timer goal, and I've to
remember: there is no upper-fwd!!
;-)
I understand but it's not the way the Standard is thought (well, at least I
think). You're supposed to process an event on the outer, which then uses
priority rules to know what's done (deeper first, completion events,
deferred events, conflicts, etc.).
Not that I have a problem "fixing" the Standard, but I'd find it hard to
implement and document some basic mechanisms. For example, say I'd upper-fwd
from a submachine in region 2 from 4. Which regions get to try handling the
event? 3, then 4? 1 - 3 - 4? 1 -2 -3 -4? 3 - 4 - 1? What do I do if while I
do this, another submachine wants to upper-fwd an event?
And what do I do if this sub-fsm has a sub-sub-fsm? Do I process up or down?
Post by Albert Gil Moreno
Post by Christophe Henry
As already said, several people asked for actions to get as parameter not
only the current fsm but also the top-level one.
I find it a good idea, I am just lacking time at the moment :(
I know, it will be great, thanks a lot for your time!!
:-)
No problem. It's going to solve many problems anyway.
Post by Albert Gil Moreno
(again, and only if you are curious, take a look to the attached
implementation...)
I did and I think you'll be satisfied with a submachine with pseudo-exit(s).
At least in your timer case. There are others where there simply is no
simple UML solution.

Cheers,
Christophe
Albert Gil Moreno
2012-04-12 08:26:21 UTC
Permalink
Thanks a lot for your time and suggestions Christophe!
Let me see if I got it right. You want a timer to count from x to 0, and when timer == 0, generate a timeout event, correct?
Then you want the possibility to reset the timer.
IIUC, you implemented your timer as a second region with a single Timer state. This looks so far ok to me because the region can process events to the state machine (for other regions).
Of course, the more complicated your timer gets, the harder it becomes.
You are totally right!
:-)
Post by Albert Gil Moreno
- A Timer inside all my (sub)state machines?
Sounds like a lot of work ;-)
agree!
;-)
Post by Albert Gil Moreno
- An independent state machine only for the Timer and (not so ugly)
pointers from/to it in all my (sub) state machines?
This looks like a perfect use case for a submachine. Define it once as an independent state machine and reuse it in any state machine needing a timer.
Yes!
In my first approach i use only an independent and reusable "state"
and not a complete "state machine", but my idea is what you say:
independent and reusable! ;-))
So far so good, but you are stuck with how to inform client state machines of your timer that there is a timeout, right?
mmm... not exactly...

In fact, the timeout is working fine.
It is sent from the timer state/region and processed fine in the
"main" machine/region, and in their submachines.

Is the reset event (only when generated in a submachine of the main
machine/region) which is really failing.

The reset is always generated when there is "activity" in the main
machine/region, where I (could) have submachines.
If there is "activity" in a submachine, the reset event is generated
in the submachine (but not up-fwd) and then is not processed by the
timer region of the upper state machine...

So, until now, the *reset in a submachine* is my real problem, not the
timeout (which work great!).
Post by Albert Gil Moreno
- More work on the pseudo exit?
- use the UML way. The timer is a state machine, with a pseudo exit when a timeout is detected. This is an encapsulated submodule used by any fsm. Like a well-behaved submodule, it advertises that timeout events are going to be fired through the pseudo exit. How it is done inside is not the outer fsm's business.
Ok, iiuc, this is a better way to define that a timeout is going to be
sent outside the state machine.
And, although now the timeout is not a problem for me, i understand
that pseudo-exit is the standard way to say "I'm a (full) state
machine but I'm sending events out of me". is this correct? (i should
read something about uml right now! ;-)

Then I should probably use them (the pseudo exists) also for the
resets generated in my submachines...
I have to read more on pseudo exits...
- no ugly pointer
- UML-conform
- perfectly reusable and encapsulated timer for different uses. Often, fsm's need a timer but differ in how they handle timeouts. Leave it to them. The documentation states the timeout is sent through a pseudo exit. Or several pseudo exit states.
- the timer is a state machine in its own right, you can write a wonderful unit test for it alone.
That's exactly what i'was looking for!
:-))

But here I'm still missing my real problem: how to (say in the
documentation and to) implement the fact that the timer can/should
receive "reset" events from any (sub) state in the orthogonal
region(s).
May be pseudo-entries...?
Post by Albert Gil Moreno
For this reason I could think that a (sub) state machine could handle
some "no_transitions" for some events (some cached cases), but it can
also auto-upper-fwd them to the parent state machine...
I understand but it's not the way the Standard is thought (well, at least I think). You're supposed to process an event on the outer, which then uses priority rules to know what's done (deeper first, completion events, deferred events, conflicts, etc.).
I see, but iiuc the standard way is fine for me!

I mean, iiuc, the key difference here between the standard and the msm
is that in the standard all events are always processed by the outest
sm (and then it uses the prioty rules), and in msm we can process
events in the "submachine scope" (in fact, until now, if we hate ugly
pointers, then we are forced to process events only in that submachine
scope, right? ;-).

Then, iiuc, if msm removes the possibility of processing events
directly into submachines but the event processing is always done
starting from the outer state machine, then everything should work
(and be more standard-conform)?
Am I missing something here?

I mean, the upper-fwd is probably a kind of bad idea needed (or
useful) only because msm allows (now it "forces") event processing
directly in submachine scopes, if not, every thing should just work?

If I'm right, then, I'm wondering if the processing of events directly
in submachines is useful in some cases...?
I mean, I see that access to the submachine can be useful if someone
has some kind of "global" data members in it (shared by all the
states) and want to access them...

But the fact of event processing, is really useful in any case?
Forcing the event processing to start always to the outest state
machine should not remove any possibility we have now... or it will?

I'm far to be sure, but now I'm thinking something like:

sm::process_event(evt)
{
if(this->is_contained()) outer->process_event(evt); // assuming we
have a pointer to the outer sm...
else process_event_with_rules(evt);
}

ok, that's upper-fwd...
;-)


Cheers!
Albert
Christophe Henry
2012-04-16 21:07:48 UTC
Permalink
Post by Albert Gil Moreno
Post by Christophe Henry
So far so good, but you are stuck with how to inform client state
machines of your timer that there is a timeout, right?
mmm... not exactly...
In fact, the timeout is working fine.
It is sent from the timer state/region and processed fine in the
"main" machine/region, and in their submachines.
Is the reset event (only when generated in a submachine of the main
machine/region) which is really failing.
The reset is always generated when there is "activity" in the main
machine/region, where I (could) have submachines.
If there is "activity" in a submachine, the reset event is generated
in the submachine (but not up-fwd) and then is not processed by the
timer region of the upper state machine...
So, until now, the *reset in a submachine* is my real problem, not the
timeout (which work great!).
I see. In this case, it's not very beautiful but you'll have to forward the
reset event from one region to the other. You might need to use a pseudo
exit, then re-enter your submachine. If you use a History, you'll get back
to the substate you were in.
Post by Albert Gil Moreno
Post by Christophe Henry
Post by Albert Gil Moreno
- More work on the pseudo exit?
- use the UML way. The timer is a state machine, with a pseudo exit when
a timeout is detected. This is an encapsulated submodule used by any fsm.
Like a well-behaved submodule, it advertises that timeout events are
going to be fired through the pseudo exit. How it is done inside is not
the outer fsm's business.
Ok, iiuc, this is a better way to define that a timeout is going to be
sent outside the state machine.
And, although now the timeout is not a problem for me, i understand
that pseudo-exit is the standard way to say "I'm a (full) state
machine but I'm sending events out of me". is this correct? (i should
read something about uml right now! ;-)
Yes and "I'm going to exit in the process".
Post by Albert Gil Moreno
Then I should probably use them (the pseudo exists) also for the
resets generated in my submachines...
I have to read more on pseudo exits...
There is not much actually. It's a bit like a return statement in a
function.
Post by Albert Gil Moreno
But here I'm still missing my real problem: how to (say in the
documentation and to) implement the fact that the timer can/should
receive "reset" events from any (sub) state in the orthogonal
region(s).
I'm afraid there is no nice way to document this. You'll have to use
standard code documentation tools.
Post by Albert Gil Moreno
May be pseudo-entries...?
I'm not a fan of them. They bring not much documentation help and mix bad
with orthogonal regions (they can enter only one region).
Post by Albert Gil Moreno
Post by Christophe Henry
Post by Albert Gil Moreno
For this reason I could think that a (sub) state machine could handle
some "no_transitions" for some events (some cached cases), but it can
also auto-upper-fwd them to the parent state machine...
I understand but it's not the way the Standard is thought (well, at least
I think). You're supposed to process an event on the outer, which then
uses priority rules to know what's done (deeper first, completion events,
deferred events, conflicts, etc.).
I see, but iiuc the standard way is fine for me!
I mean, iiuc, the key difference here between the standard and the msm
is that in the standard all events are always processed by the outest
sm (and then it uses the prioty rules), and in msm we can process
events in the "submachine scope" (in fact, until now, if we hate ugly
pointers, then we are forced to process events only in that submachine
scope, right? ;-).
Nope. The Standard actually says very little about queues (except that the
order of dequeuing is up to the author) and I could not find a word about
how a state machine or submachine can process an event to itself. This one
can only be found in the litterature, and I saw it only in the context of a
simple non-hierarchical state machine. How a submachine has to process an
event it sends to itself is nowhere described, which means left to the
author ;-)
But yes to the second point, at the moment msm allows you to process an
event at any level, but for top-level, you will need the ugly pointer.
Post by Albert Gil Moreno
Then, iiuc, if msm removes the possibility of processing events
directly into submachines but the event processing is always done
starting from the outer state machine, then everything should work
(and be more standard-conform)?
Am I missing something here?
It is already standard-conform as not forbidden :)
But if I change this, then it might become non standard-conform (as
previously said, if the submachine has a subsubmachine and I process up, I
violate the priority rules).
Post by Albert Gil Moreno
I mean, the upper-fwd is probably a kind of bad idea needed (or
useful) only because msm allows (now it "forces") event processing
directly in submachine scopes, if not, every thing should just work?
If I'm right, then, I'm wondering if the processing of events directly
in submachines is useful in some cases...?
I mean, I see that access to the submachine can be useful if someone
has some kind of "global" data members in it (shared by all the
states) and want to access them...
But the fact of event processing, is really useful in any case?
Forcing the event processing to start always to the outest state
machine should not remove any possibility we have now... or it will?
It will be slower as you will need to process from fsm to sub-fsm to
sub-fsm, etc.
Say I'm writing a parser, I'll be unhappy to have msm force me to process
all from the top, handle conflicts, etc.
Then it'll be confusing, because my submachine will handle events very
differently according to the configuration (used as stand-alone, embedded,
with or without submachines). Worse, If I templatize a submachine with a
subsubstate type (which could be a submachine), it will behave quite
differently.
Third, chances are good that I will violate a priority rule.
And fourth, I will break existing code.
Post by Albert Gil Moreno
sm::process_event(evt)
{
if(this->is_contained()) outer->process_event(evt); // assuming we
have a pointer to the outer sm...
else process_event_with_rules(evt);
}
ok, that's upper-fwd...
;-)
And if outer has a outer itself, what happens?
I think there is no general solution. MSM makes the choice that the "fsm"
parameter to actions is the deepest level for performance and style reasons.
I understand that there are cases where it's not what you wish. For this
cases, I favor instead in actions some variadic template where you get all
the fsms on the way, from deepest to outer. This way you can choose what is
the best depth for you to process an event. But it's not implemented yet, so
in the meantime, well, there is an ugly pointer ;-)
(plus knowing the outer pointer type will be pretty hard with C++ but it's
an implementation detail)

Cheers,
Christophe
Albert Gil Moreno
2012-04-17 08:55:44 UTC
Permalink
Thanks Christophe!

I understand the reasons (I was totally forgetting performance!).

Anyway, I've found myself in very similar situations (needing events from
submachines to the top machine) and I've decided to "standarize" a ugly
pointer solution just to make it nicer... ;-)

It works great and I think it will be easy to adapt to the future variadic
template you are plannig to add.

Thanks for the MSM Christophe!


Albert

Loading...