[statechart] forward_event()
Hi, I have a state [A] with three orthogonal substates [X,Y,Z]. The states [A] and [X] provide custom reactions for an event [E]. If processing event [E] the state machine calls first the custom reaction in [A]. This method returns with forward_event() ; After this the custom reaction in [A] is called again. And then custom reaction in [X] is called. This is a little cumbersome. Why is the custom reaction in [A] called twice? And why is the outermost state called first? It would be something easier to work vice versa. First the more "specialized" inner states could do there work and then the "generalized" outer states can do the everything else. Pirx!
I have a state [A] with three orthogonal substates [X,Y,Z]. The states [A] and [X] provide custom reactions for an event [E]. If processing event [E] the state machine calls first the custom reaction in [A]. This method returns with forward_event() ; After this the custom reaction in [A] is called again. And then custom reaction in [X] is called.
From my experience, it doesn't behave like that.
It would be something easier to work vice versa. First the more "specialized" inner states could do there work and then the "generalized" outer states can do the everything else.
That's what happens usually. Maybe there's some problem with your FSM definition?
"Igor R"
From my experience, it doesn't behave like that.
It would be something easier to work vice versa. First the more
"specialized" inner states could do there work and then the "generalized"
outer states can do the everything else.
That's what happens usually.
Maybe there's some problem with your FSM definition?
Hi,
you're right. I've wrote a simplified example and all works like expected.
But can't currently find any bug in my definitions. Here is the working
simplified example:
#include
{ typedef boost::mpl::list < boost::statechart::custom_reaction< E >
reactions; boost::statechart::result react( const E& ); };
struct X : boost::statechart::simple_state< X , A::orthogonal< 0 >
{ typedef boost::mpl::list < boost::statechart::custom_reaction< E >
reactions; boost::statechart::result react( const E& ); }; struct Y : boost::statechart::simple_state< Y , A::orthogonal< 1 >
{}; struct Z : boost::statechart::simple_state< Z , A::orthogonal< 2 >
{}; } #include "statechart.h" machine::machine() { state_machine_t::initiate(); } bool machine::unit_test() { machine m; m.process_event(E()); return true; } namespace state { boost::statechart::result A::react( const E& evt ) { std::cout << "A" << std::endl; return discard_event(); } boost::statechart::result X::react( const E& evt ) { std::cout << "X" << std::endl; return forward_event(); } } // end namespace state Pirx!
"Igor R"
But can't currently find any bug in my definitions. Here is the working simplified example:
By saying "working" here, you mean that the example you posted works correctly or it reproduces the problem you described? Hi, I mean that the example I posted works correct. It contains no errors nor is it different from what I expected. Sorry, for my misleading statements. Currently I try to extend this example to reproduce the decribed behaviour. I have a complex state machine with more than 40 states, substates, orthogonal states and much more events and it's hard work to track down the code to a small piece that shows whats going wrong. Note: I observed that the behaviour was slightly different after a complete recompile of the project. Possibly doesn't VC9 a good job here. Will test it with the gcc too. Pirx!
Currently I try to extend this example to reproduce the decribed behaviour.
The behavior you describe in your OP *and* the behavior of your example is as expected. The reason lies in the fact that the order in which innermost states are checked for reactions is arbitrary. So, if the order is X, Y, Z, the behavior is as in your example, if it is either Y, Z, X or Z, Y, X then the behavior is as in your OP. HTH, -- Andreas Huber When replying by private email, please remove the words spam and trap from the address shown in the header.
Hi
I have a state [A] with three orthogonal substates [X,Y,Z]. The states [A] and [X] provide custom reactions for an event [E]. If processing event [E] the state machine calls first the custom reaction in [A]. This method returns with forward_event() ; After this the custom reaction in [A] is called again. And then custom reaction in [X] is called.
This is a little cumbersome. Why is the custom reaction in [A] called twice?
And why is the outermost state called first? It would be something easier to work vice versa. First the more "specialized" inner states could do there work and then the "generalized" outer states can do the everything else.
This behavior is as designed, for an in-depth description please see: http://www.boost.org/libs/statechart/doc/reference.html#process_event Reaction search always starts with an *arbitrary* innermost state and then first works its way outward. Only when the outermost state has been reached is the next innermost state checked for a suitable reaction. This behavior stems form the fact that all reactions defined in outer states are "inherited" by their direct and indirect inner states, as mandated by the UML standard. What are you trying to achieve? Maybe I can suggest a more suitable implementation. HTH, -- Andreas Huber When replying by private email, please remove the words spam and trap from the address shown in the header.
Reaction search always starts with an *arbitrary* innermost state and then first works its way outward. Only when the outermost state has been reached is the next innermost state checked for a suitable reaction. This behavior stems form the fact that all reactions defined in outer states are "inherited" by their direct and indirect inner states, as mandated by the UML standard.
Oh, I completely missed this point too. This describes the issue I encountered long time ago: http://lists.boost.org/boost-users/2008/08/38931.php In that case an out state deferred some event, while one of the inner orthogonal states wished to process it - but the event never reached this inner state. Now I understand that the other orthogonal states actually *deferred* that event ("inheriting" this definition from the outer state)...
Hi all,
thanks for the help. I compiled the project with GCC. The behaviour is
unchanged. Furthermore I'm not able to reproduce the behaviour in a
simplified demo code. I've even tried to debug the statechart library and
read the
http://www.boost.org/doc/libs/1_40_0/libs/statechart/doc/reference.html#proc...
documentation carefully. But I must honestly say, I have not quite
understood. Thats why I have a simple question: Is it guaranteed that the
innermost states are called first? If yes, I have a bug (hard to detect). If
no, I have to rewrite my code.
Note: When working with the statechart libraries I got some ideas to improve
the "usability". Think about it as a wishlist.
1. Debugging features, like in the Spirit Library. e.g. easy naming of
states, tracing the event route, more debug output (verbose mode)
2. Calling the state constructor with an event. something like transit<S>(
event );
3. A process_event() method that acceepts an intrusive pointer. Currently
each event will be duplicated (clone()).
4. A thread safe scheduler. It's hard to use hundreds of instances of the
asynchronous_state_machine because each instance needs a thread. Correct me
if I am wrong here.
5. Macros to define events, where all memebers are const. I've found that
it's error prone to write it by hand again and again.
6. Thanks!
Pirx!
"Andreas Huber"
Hi
I have a state [A] with three orthogonal substates [X,Y,Z]. The states [A] and [X] provide custom reactions for an event [E]. If processing event [E] the state machine calls first the custom reaction in [A]. This method returns with forward_event() ; After this the custom reaction in [A] is called again. And then custom reaction in [X] is called.
This is a little cumbersome. Why is the custom reaction in [A] called twice?
And why is the outermost state called first? It would be something easier to work vice versa. First the more "specialized" inner states could do there work and then the "generalized" outer states can do the everything else.
This behavior is as designed, for an in-depth description please see:
http://www.boost.org/libs/statechart/doc/reference.html#process_event
Reaction search always starts with an *arbitrary* innermost state and then first works its way outward. Only when the outermost state has been reached is the next innermost state checked for a suitable reaction. This behavior stems form the fact that all reactions defined in outer states are "inherited" by their direct and indirect inner states, as mandated by the UML standard.
What are you trying to achieve? Maybe I can suggest a more suitable implementation.
HTH,
-- Andreas Huber
When replying by private email, please remove the words spam and trap from the address shown in the header.
"Commander Pirx"
thanks for the help. I compiled the project with GCC. The behaviour is unchanged. Furthermore I'm not able to reproduce the behaviour in a simplified demo code. I've even tried to debug the statechart library and read the http://www.boost.org/doc/libs/1_40_0/libs/statechart/doc/reference.html#proc... documentation carefully. But I must honestly say, I have not quite understood. Thats why I have a simple question: Is it guaranteed that the innermost states are called first?
It depends :-). Seriously, e.g. in your demo program, there's *no* guarantee that the reaction in state X is called first. Whether it is or it isn't depends on which innermost states is checked first for a suitable reaction. If X is checked first, then you get the behavior of your demo program. If either Y or Z is checked first then you get the behavior you observe in your application. Exactly which innermost state is checked first is *arbitrary*. More importantly, if the first checked innermost state does not define a reaction for the event, then its direct *outer* state is checked next (state A in your example prog). Hope this clears things up, if not please let me know.
Note: When working with the statechart libraries I got some ideas to improve the "usability". Think about it as a wishlist.
1. Debugging features, like in the Spirit Library. e.g. easy naming of states, tracing the event route, more debug output (verbose mode)
I'll see what I can do, however in any case this will definitely not make it into the next release, as other stuff is already pending.
2. Calling the state constructor with an event. something like transit<S>( event );
You already can pass the event to the transit function, but the event is never passed to any state constructor (only to the transition action). This is a very popular request, but I'm afraid this will not be possible. The problem is that the state constructor can be called as a result of a number of different events. However, I plan to support a work-around (triggering_event, see to-do list) for the next release.
3. A process_event() method that acceepts an intrusive pointer. Currently each event will be duplicated (clone()).
No, it should only be cloned if the machine defers the event. Also, if you allocate your event with new and assign it to an intrusive_ptr p and then pass *p to process_event() then the event should not be cloned.
4. A thread safe scheduler. It's hard to use hundreds of instances of the asynchronous_state_machine because each instance needs a thread. Correct me if I am wrong here.
? A single fifo_scheduler can service as many asynchronous state machines as you like.
5. Macros to define events, where all memebers are const. I've found that it's error prone to write it by hand again and again.
You mean the macro would save you from having to write const for each member? HTH, -- Andreas Huber When replying by private email, please remove the words spam and trap from the address shown in the header.
1. Debugging features, like in the Spirit Library. e.g. easy naming of
states, tracing the event route, more debug output (verbose mode)
I'll see what I can do, however in any case this will definitely not make it into the next release, as other stuff is already pending.
I'd like to join this request -- it would be really great if we could trace an event route. Sometimes I find myself debugging rather complex async FSMs deep inside Statechart templates, just to find some little stupid mistake in my reaction definitions...
Hi, first thanks for the fast reply. Your description was helpfull:
It depends :-). Seriously, e.g. in your demo program, there's *no* guarantee that the reaction in state X is called first. Whether it is or it isn't depends on which innermost states is checked first for a suitable reaction. If X is checked first, then you get the behavior of your demo program. If either Y or Z is checked first then you get the behavior you observe in your application. Exactly which innermost state is checked first is *arbitrary*. More importantly, if the first checked innermost state does not define a reaction for the event, then its direct *outer* state is checked next (state A in your example prog). Hope this clears things up, if not please let me know.
Now I get it. This is, was I saw in the debugger. OK. It's a little messy for me. I'm afraid that there is no way, to change this. I'm thinking about a special reaction in these orthogonal states to route the event "sidewards" instead of downwards. Any chance? Second the wishlist:
2. Calling the state constructor with an event. something like transit<S>( event );
You already can pass the event to the transit function, but the event is never passed to any state constructor (only to the transition action). This is a very popular request, but I'm afraid this will not be possible. The problem is that the state constructor can be called as a result of a number of different events. However, I plan to support a work-around (triggering_event, see to-do list) for the next release.
I'm curious.
3. A process_event() method that acceepts an intrusive pointer. Currently each event will be duplicated (clone()).
No, it should only be cloned if the machine defers the event. Also, if you allocate your event with new and assign it to an intrusive_ptr p and then pass *p to process_event() then the event should not be cloned.
Good point. It never occurred to me.
4. A thread safe scheduler. It's hard to use hundreds of instances of the asynchronous_state_machine because each instance needs a thread. Correct me if I am wrong here.
? A single fifo_scheduler can service as many asynchronous state machines as you like.
Yes, you're right. I was thinking about a state machine that isn't bound to one thread. I had the strand concept from the asio library in mind. You can post a work item (event) asynchonous but have a guarantee that execution is serialized.
5. Macros to define events, where all memebers are const. I've found that it's error prone to write it by hand again and again.
You mean the macro would save you from having to write const for each member?
Something like this: #define DECLARE_SIMPLE_EVENT(e) \ struct e : boost::statechart::event< e > \ { event_tracer tracer_; \ e() : tracer_("event."#e) {}}; // declare an event with 1 parameter #define DECLARE_EVENT_1(e,v) \ struct e : boost::statechart::event< e > \ { e(v); e(const e&); \ const v v_; \ event_tracer tracer_; \ }; #define DEFINE_EVENT_1(e,v) \ e::e(v p) \ : v_(p) \ , tracer_("event."#e) {} \ e::e(const e& copy) \ : v_(copy.v_) \ , tracer_("event.copy."#e) {} Ignore the tracer_. This is only a helper object to trace the event ctor/dtor. I think it's a good idea to keep all members of an event const. Pirx!
Now I get it. This is, was I saw in the debugger. OK. It's a little messy for me. I'm afraid that there is no way, to change this. I'm thinking about a special reaction in these orthogonal states to route the event "sidewards" instead of downwards. Any chance?
UML is quite clear on the order how reactions are checked (always from the/an innermost state outward). Now, if we come to the conclusion that your use-case is sufficiently common *and* if there is no easier way to achieve the behavior you want, I might consider adding such a beast. But again, that'll be 1.42 *at* *best*.
From a high-level POV, could you please describe the behavior you are trying to implement? If you are worried about posting app details in a public forum, you can send it to me by PM. Thanks.
? A single fifo_scheduler can service as many asynchronous state machines as you like.
Yes, you're right. I was thinking about a state machine that isn't bound to one thread. I had the strand concept from the asio library in mind. You can post a work item (event) asynchonous but have a guarantee that execution is serialized.
Ok, can you implement what you want with the current version or do you require different behavior? [snip]
Ignore the tracer_. This is only a helper object to trace the event ctor/dtor. I think it's a good idea to keep all members of an event const.
In general, I tend to agree. However in this case making the data members const doesn't really add much, as events are always passed as const to reactions anyway. So, I'm not sure whether such a macro would be used much. -- Andreas Huber When replying by private email, please remove the words spam and trap from the address shown in the header.
However, I plan to support a work-around (triggering_event, see to-do list) for the next release.
I've just noticed that 1.41 is already closed for anything other than bug fixes. So triggering_event will only be in 1.42. However, I plan to implement it before the end of October, so those in urgent need can get it from the trunk. Sorry, -- Andreas Huber When replying by private email, please remove the words spam and trap from the address shown in the header.
participants (3)
-
Andreas Huber
-
Commander Pirx
-
Igor R