Fwd: [msm] guard behavior if guard guards the transition MSM threats event as handled is that correct ?

Hi Christophe the attached example I think shows 2 problems. 1st is that the deferral of event3 is not happening. 2nd is that the no_transition function is never called. the output of the run is : MS1 Entering State: Idle by: event1 MS1 Leaving State: Idle by: event1 MS1 Entering State: SubRunning by: event1 FwdGuard: returns:1, event: event2 Processing BaseEvent Instance(event2) Setting guard false FwdGuard: returns:0, event: event3 FwdGuard: returns:0, event: event4 FwdGuard: returns:0, event: event5 MS1 Leaving State: SubRunning by: event5 MS1 Entering State: AfterSub by: event5 I think it shall be : MS1 Entering State: Idle by: event1 MS1 Leaving State: Idle by: event1 MS1 Entering State: SubRunning by: event1 FwdGuard: returns:1, event: event2 Processing BaseEvent Instance(event2) Setting guard false FwdGuard: returns:0, event: event3 FwdGuard: returns:0, event: event4 MS1 no_transition event (event4) MS1 Leaving State: SubRunning by: event5 MS1 Entering State: AfterSub by: event5 MS1 no_transition event (event3) So in this example event3 and event4 are never triggered a transition ... the Row < SubRunning , baseEvent , none , ProcessBaseEvent , FwdGuard
was guarded by the FwdGuard for these events.
Is my expected behavior wrong ? If guard is executed but the transition actually is triggered because it is guarded out why this event treated as handled event ?. This 2 problems breaks my current nested state machine implementation because I'm using no_transition to forward the event to the outer state machine. I'm confused :(. Cheers Richie

Hi Richard,
the attached example I think shows 2 problems.
1st is that the deferral of event3 is not happening. 2nd is that the no_transition function is never called.
the output of the run is :
MS1 Entering State: Idle by: event1 MS1 Leaving State: Idle by: event1 MS1 Entering State: SubRunning by: event1 FwdGuard: returns:1, event: event2 Processing BaseEvent Instance(event2) Setting guard false FwdGuard: returns:0, event: event3 FwdGuard: returns:0, event: event4 FwdGuard: returns:0, event: event5 MS1 Leaving State: SubRunning by: event5 MS1 Entering State: AfterSub by: event5
I think it shall be :
MS1 Entering State: Idle by: event1 MS1 Leaving State: Idle by: event1 MS1 Entering State: SubRunning by: event1 FwdGuard: returns:1, event: event2 Processing BaseEvent Instance(event2) Setting guard false FwdGuard: returns:0, event: event3 FwdGuard: returns:0, event: event4 MS1 no_transition event (event4) MS1 Leaving State: SubRunning by: event5 MS1 Entering State: AfterSub by: event5 MS1 no_transition event (event3)
So in this example event3 and event4 are never triggered a transition ... the Row < SubRunning , baseEvent , none , ProcessBaseEvent , FwdGuard
was guarded by the FwdGuard for these events.
Is my expected behavior wrong ?
Well, hard to answer because the Standard does not discuss conflicts between a state declaration and a transition, so your guess is as good as mine ;-) Let's say that this is the intended behavior. In the current implementation, the state declaration has the lowest priority. This means the transition table comes first, and if it handles the event, then processing stops here so your deferred declaration has no effect. The answer to the question in your title is yes. If a guard rejects the event, the event is really handled (rejection is a perfectly acceptable event handling) so there is no call to no_transition, which is to be seen as a catch-all error handler in case you forget to handle an event. Seeing that the default version is an assert, imagine what you'd get at every guard rejection ;-)
If guard is executed but the transition actually is triggered because it is guarded out why this event treated as handled event ?.
Because a guard is not an error, a call to no_transition probably is. I think the best way to solve the conflict between state declaration and the transition table wanting to process is to state what you want in the transition table itself: Row < SubRunning , event3 , none , Defer , none /* or guard */ > will do. Well, there is a small bug, but not where you expect it. It seems that the processing continues even though this transition defers the event. I just fixed this in the trunk (rev. 75641).
This 2 problems breaks my current nested state machine implementation because I'm using no_transition to forward the event to the outer state machine. I'm confused :(.
I think this will not work (and should not) because there is no call to no_transition in a submachine (I don't see it here but I suppose your real code is bigger). The reason is the same as above, no_transition is an error and will assert. But if a submachine cannot process an event, it is not necessarily an error, the outer machine should get its chance to process the event. I'm also afraid you're trying to keep a pointer to the outer machine to process the event (cycle). I suggest pushing the event to a pseudo exit, which will achieve the same a better way. HTH, Christophe

Hi Christophe I'm using boost trunk from yesterday And I have modified my example to use your suggestion : Row < SubRunning , event3 , none , Defer , none /* or guard */ > will do. Well, there is a small bug, but not where you expect it. It seems that the processing continues even though this transition defers the event. I just fixed this in the trunk (rev. 75641). Here is the execution output of the example attached : MS1 Entering State: Idle by: event1 MS1 Leaving State: Idle by: event1 MS1 Entering State: SubRunning by: event1 FwdGuard: returns:1, event: event2 Processing BaseEvent Instance(event2) Setting guard false FwdGuard: returns:0, event: event3 MS1 DEFERED: event3 FwdGuard: returns:0, event: event4 FwdGuard: returns:0, event: event3 MS1 DEFERED: event3 FwdGuard: returns:0, event: event5 MS1 Leaving State: SubRunning by: event5 MS1 Entering State: AfterSub by: event5 MS1 no_transition event (event3) It seems that the bug not fixed ... but this is my least worries ( partly my fault You have asked me to test it for you .... but I did not sorry about that). My bigger problem is the interpretation of no_transition you saying that it is an error situation... Well I have in my code a lot of situation when there are signals which are not interested to my state machine but they are dispatched to me anyhow our modules are used by different places for different purposes and there are signals which are useful in one case but not needed for my purpose I just simply ignore them these are ending up in no_transition so there I just write a log entry that signal is not handled so for me it is not an error at all it is the intended behavior. Now back to the My blocking problem ..... We use at the moment about 36 MSM state machines out of this 26 are performing specific well defined configuration task these 26 state-machine are nested to each in to each other to perform complicated behaviors sometimes there are nesting goes done to 5 level deep. We have, I call them main state machines which are connect these small task to each other to build complex behavior. We have more than 500 different row entries spitted in to these state machines. At the beginning we try to use MSM way of state machine nesting and soon we figured out that there is no compiler in the word which can compile/link this code. Than I have decided to make smaller compile unit and make each state machine as a separate compile unit and let communicate them over a simple interface. Yes this slows done the dispatching because we have to re-dispatch each event in run-time as many times as many nesting level we have. But this tread of is acceptable. And yes this way state machines are holding references to outer state machines if it is running as a nested state machine and states which are nested state machines are holding references to inner state machine instances. Every state machine uses the same interface to communicate and we hide the construction of the state machines with factories. So this way the outer does not exposed to the inner the inner does not exposed to the outer leads us to a compile-able code :). In case the state machine is in a stare where the sate is actually a holder of an inner state machine there is a Row with a baseEvent which this case re-dispatches the event to the inner one. Now if the inner one does not handles the event it shall send it back to the outer one. My assumption was that I can use the no_transition for this purpose but the implementation turns out different :(. For me name no_transition suggests that if no transition triggered. This function will be called and if the guard rejects the transition than this means to me that the transition is not triggered. Anyhow we could argue on it but in the end it does not help with my problem .... I would like to ask you to change the implementation .... if it is possible ..... :) I think my way would help in case of debugging because if the state machine does not triggered a transition at least the user has a chance to log it or do something with it .... I find it very annoying if an event is "eaten by a state machine without knowing what happened" If you disagree than please help me to find a way to get around of my problem .... I see 3 way out of it : 1. change the implementation of no_transition triggering. 2. provide a new function something like default_transition which is called if the transaction table( including rejected transitions by guards ) or states (deferral) are not handling the event. 3. A special row something like Row < AllState, AllEvent, None, MYDefaultTrans, None> eg give a last default trans option to the user to handle the event. Or if you have any better idea how to compile my code ?. Thanks for your help .... Cheers Richard On 23 November 2011 21:49, Christophe Henry <christophe.j.henry@googlemail.com> wrote:
Hi Richard,
the attached example I think shows 2 problems.
1st is that the deferral of event3 is not happening. 2nd is that the no_transition function is never called.
the output of the run is :
MS1 Entering State: Idle by: event1 MS1 Leaving State: Idle by: event1 MS1 Entering State: SubRunning by: event1 FwdGuard: returns:1, event: event2 Processing BaseEvent Instance(event2) Setting guard false FwdGuard: returns:0, event: event3 FwdGuard: returns:0, event: event4 FwdGuard: returns:0, event: event5 MS1 Leaving State: SubRunning by: event5 MS1 Entering State: AfterSub by: event5
I think it shall be :
MS1 Entering State: Idle by: event1 MS1 Leaving State: Idle by: event1 MS1 Entering State: SubRunning by: event1 FwdGuard: returns:1, event: event2 Processing BaseEvent Instance(event2) Setting guard false FwdGuard: returns:0, event: event3 FwdGuard: returns:0, event: event4 MS1 no_transition event (event4) MS1 Leaving State: SubRunning by: event5 MS1 Entering State: AfterSub by: event5 MS1 no_transition event (event3)
So in this example event3 and event4 are never triggered a transition ... the Row < SubRunning , baseEvent , none , ProcessBaseEvent , FwdGuard
was guarded by the FwdGuard for these events.
Is my expected behavior wrong ?
Well, hard to answer because the Standard does not discuss conflicts between a state declaration and a transition, so your guess is as good as mine ;-)
Let's say that this is the intended behavior. In the current implementation, the state declaration has the lowest priority. This means the transition table comes first, and if it handles the event, then processing stops here so your deferred declaration has no effect.
The answer to the question in your title is yes. If a guard rejects the event, the event is really handled (rejection is a perfectly acceptable event handling) so there is no call to no_transition, which is to be seen as a catch-all error handler in case you forget to handle an event. Seeing that the default version is an assert, imagine what you'd get at every guard rejection ;-)
If guard is executed but the transition actually is triggered because it is guarded out why this event treated as handled event ?.
Because a guard is not an error, a call to no_transition probably is. I think the best way to solve the conflict between state declaration and the transition table wanting to process is to state what you want in the transition table itself:
Row < SubRunning , event3 , none , Defer , none /* or guard */ >
will do.
Well, there is a small bug, but not where you expect it. It seems that the processing continues even though this transition defers the event. I just fixed this in the trunk (rev. 75641).
This 2 problems breaks my current nested state machine implementation because I'm using no_transition to forward the event to the outer state machine. I'm confused :(.
I think this will not work (and should not) because there is no call to no_transition in a submachine (I don't see it here but I suppose your real code is bigger). The reason is the same as above, no_transition is an error and will assert. But if a submachine cannot process an event, it is not necessarily an error, the outer machine should get its chance to process the event. I'm also afraid you're trying to keep a pointer to the outer machine to process the event (cycle). I suggest pushing the event to a pseudo exit, which will achieve the same a better way.
HTH, Christophe
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

Here is the execution output of the example attached :
MS1 Entering State: Idle by: event1 MS1 Leaving State: Idle by: event1 MS1 Entering State: SubRunning by: event1 FwdGuard: returns:1, event: event2 Processing BaseEvent Instance(event2) Setting guard false FwdGuard: returns:0, event: event3 MS1 DEFERED: event3 FwdGuard: returns:0, event: event4 FwdGuard: returns:0, event: event3 MS1 DEFERED: event3 FwdGuard: returns:0, event: event5 MS1 Leaving State: SubRunning by: event5 MS1 Entering State: AfterSub by: event5 MS1 no_transition event (event3)
It seems that the bug not fixed ...
Sorry, I should have been more explicit (and avid sheating to save a guard). You need to invert your 2 transitions to get the desired effect (processing is done from the bottom of the table): Row < SubRunning , baseEvent , none , ProcessBaseEvent , FwdGuard >, Row < SubRunning , event3 , none , LogDefer , none
Now, deferring has a higher priority, which gives the following desired output: MS1 Entering State: Idle by: event1 MS1 Leaving State: Idle by: event1 MS1 Entering State: SubRunning by: event1 FwdGuard: returns:1, event: event2 Processing BaseEvent Instance(event2) Setting guard false MS1 DEFERED: event3 // ok, defer event3 FwdGuard: returns:0, event: event4 //event4 is not deferred MS1 DEFERED: event3 // event4 processed, can we process event3? Nope, defer again FwdGuard: returns:0, event: event5 MS1 Leaving State: SubRunning by: event5 MS1 Entering State: AfterSub by: event5 // we changed state, can we process event3? MS1 no_transition event (event3) // yes but we have no transition, call handler
but this is my least worries ( partly my fault You have asked me to test it for you .... but I did not sorry about that).
My bigger problem is the interpretation of no_transition you saying that it is an error situation... Well I have in my code a lot of situation when there are signals which are not interested to my state machine but they are dispatched to me anyhow our modules are used by different places for different purposes and there are signals which are useful in one case but not needed for my purpose I just simply ignore them these are ending up in no_transition so there I just write a log entry that signal is not handled so for me it is not an error at all it is the intended behavior.
State machines have a long history in the hardware world. And there it is tradition that if no transition processes an event, it is an error. Purists spend a lot of time handling every event in every state and even have state tables to check this. This means that to be completely correct, we should write an internal transition in every state for every event to document we choose to ignore these events (I try to do it regularly and it does help me from time to time). The UML Standard kept all this. When a transition could handle the event but rejects it, the event is processed. For once the Standard is about clear... ;-) But there are solutions to your problem in MSM's toolbox.
Now back to the My blocking problem .....
We use at the moment about 36 MSM state machines
:)
out of this 26 are performing specific well defined configuration task these 26 state-machine are nested to each in to each other to perform complicated behaviors sometimes there are nesting goes done to 5 level deep. We have, I call them main state machines which are connect these small task to each other to build complex behavior. We have more than 500 different row entries spitted in to these state machines. At the beginning we try to use MSM way of state machine nesting and soon we figured out that there is no compiler in the word which can compile/link this code.
Admittedly. We'll have to be patient.
Than I have decided to make smaller compile unit and make each state machine as a separate compile unit and let communicate them over a simple interface. Yes this slows done the dispatching because we have to re-dispatch each event in run-time as many times as many nesting level we have. But this tread of is acceptable. And yes this way state machines are holding references to outer state machines if it is running as a nested state machine and states which are nested state machines are holding references to inner state machine instances. Every state machine uses the same interface to communicate and we hide the construction of the state machines with factories. So this way the outer does not exposed to the inner the inner does not exposed to the outer leads us to a compile-able code :).
I personally use callbacks with boost::function or signals to avoid cycles but yes, it is still a logical cycle.
In case the state machine is in a stare where the sate is actually a holder of an inner state machine there is a Row with a baseEvent which this case re-dispatches the event to the inner one. Now if the inner one does not handles the event it shall send it back to the outer one. My assumption was that I can use the no_transition for this purpose but the implementation turns out different :(. For me name no_transition suggests that if no transition triggered. This function will be called and if the guard rejects the transition than this means to me that the transition is not triggered. Anyhow we could argue on it but in the end it does not help with my problem ....
Sure. I see other ways out of your problem. I think they are elegant and conform to the spirit of state machines: - exit points, as previously said. Just write another region as last, which will process your event and send it out to the outer. This has the disadvantage to force you to check is the event has been processed or not. - internal transitions in the submachine itself. IIUC, SubRunning is meant to be a submachine, right? Then we only need to add this to its definition: struct internal_transition_table : mpl::vector< Internal < event3 , Action , Guard > > {}; (supposing you need a guard, this is optional). What does this mean? We added to your submachine (or substate) an internal transition table. By convention, MSM decides this is less "inner" than a standard transition table, so the transition table is tried first. If no transition from the transition table processes the event (or if the event is rejected by guards), then the internal table is tried. This can be easily used to replace your use of no_transition, it is more elegant, Standard-conforming and you have extra capabilities (like having different handlers, conflicting ones solved by guards, etc). I find this quite fun :) The whole internal transitions have been rewritten in 1.48 to allow this to work for all machines so you will need it or trunk (sadly this also means that while I tested as well as I could, I cannot exclude the possibility of a bug). HTH, Christophe

Hi Christophe
Sure. I see other ways out of your problem. I think they are elegant and conform to the spirit of state machines: - exit points, as previously said. Just write another region as last, which will process your event and send it out to the outer. This has the disadvantage to force you to check is the event has been processed or not. - internal transitions in the submachine itself. IIUC, SubRunning is meant to be a submachine, right? Then we only need to add this to its definition:
struct internal_transition_table : mpl::vector< Internal < event3 , Action , Guard > > {};
(supposing you need a guard, this is optional). What does this mean? We added to your submachine (or substate) an internal transition table. By convention, MSM decides this is less "inner" than a standard transition table, so the transition table is tried first. If no transition from the transition table processes the event (or if the event is rejected by guards), then the internal table is tried. This can be easily used to replace your use of no_transition, it is more elegant, Standard-conforming and you have extra capabilities (like having different handlers, conflicting ones solved by guards, etc). I find this quite fun :)
Great Great Great .... you are always 1 step a head of me .... Yes this sunds a nice solution and even I can hide the internal transition table in ta sub state machine running base state all states creating sub states are just can use this base state .... This is cool I hope internal_transition_table allows to use baseEvents as well like outer ones ...
The whole internal transitions have been rewritten in 1.48 to allow this to work for all machines so you will need it or trunk (sadly this also means that while I tested as well as I could, I cannot exclude the possibility of a bug).
Don't worry I will come back to you if it is buggy ... I think with our virtual test system we will find problems quick if there any ... Thanks for the support Cheers Richie On 24 November 2011 22:21, Christophe Henry <christophe.j.henry@googlemail.com> wrote:
Here is the execution output of the example attached :
MS1 Entering State: Idle by: event1 MS1 Leaving State: Idle by: event1 MS1 Entering State: SubRunning by: event1 FwdGuard: returns:1, event: event2 Processing BaseEvent Instance(event2) Setting guard false FwdGuard: returns:0, event: event3 MS1 DEFERED: event3 FwdGuard: returns:0, event: event4 FwdGuard: returns:0, event: event3 MS1 DEFERED: event3 FwdGuard: returns:0, event: event5 MS1 Leaving State: SubRunning by: event5 MS1 Entering State: AfterSub by: event5 MS1 no_transition event (event3)
It seems that the bug not fixed ...
Sorry, I should have been more explicit (and avid sheating to save a guard). You need to invert your 2 transitions to get the desired effect (processing is done from the bottom of the table):
Row < SubRunning , baseEvent , none , ProcessBaseEvent , FwdGuard >, Row < SubRunning , event3 , none , LogDefer , none >
Now, deferring has a higher priority, which gives the following desired output:
MS1 Entering State: Idle by: event1 MS1 Leaving State: Idle by: event1 MS1 Entering State: SubRunning by: event1 FwdGuard: returns:1, event: event2 Processing BaseEvent Instance(event2) Setting guard false MS1 DEFERED: event3 // ok, defer event3 FwdGuard: returns:0, event: event4 //event4 is not deferred MS1 DEFERED: event3 // event4 processed, can we process event3? Nope, defer again FwdGuard: returns:0, event: event5 MS1 Leaving State: SubRunning by: event5 MS1 Entering State: AfterSub by: event5 // we changed state, can we process event3? MS1 no_transition event (event3) // yes but we have no transition, call handler
but this is my least worries ( partly my fault You have asked me to test it for you .... but I did not sorry about that).
My bigger problem is the interpretation of no_transition you saying that it is an error situation... Well I have in my code a lot of situation when there are signals which are not interested to my state machine but they are dispatched to me anyhow our modules are used by different places for different purposes and there are signals which are useful in one case but not needed for my purpose I just simply ignore them these are ending up in no_transition so there I just write a log entry that signal is not handled so for me it is not an error at all it is the intended behavior.
State machines have a long history in the hardware world. And there it is tradition that if no transition processes an event, it is an error. Purists spend a lot of time handling every event in every state and even have state tables to check this. This means that to be completely correct, we should write an internal transition in every state for every event to document we choose to ignore these events (I try to do it regularly and it does help me from time to time). The UML Standard kept all this. When a transition could handle the event but rejects it, the event is processed. For once the Standard is about clear... ;-) But there are solutions to your problem in MSM's toolbox.
Now back to the My blocking problem .....
We use at the moment about 36 MSM state machines
:)
out of this 26 are performing specific well defined configuration task these 26 state-machine are nested to each in to each other to perform complicated behaviors sometimes there are nesting goes done to 5 level deep. We have, I call them main state machines which are connect these small task to each other to build complex behavior. We have more than 500 different row entries spitted in to these state machines. At the beginning we try to use MSM way of state machine nesting and soon we figured out that there is no compiler in the word which can compile/link this code.
Admittedly. We'll have to be patient.
Than I have decided to make smaller compile unit and make each state machine as a separate compile unit and let communicate them over a simple interface. Yes this slows done the dispatching because we have to re-dispatch each event in run-time as many times as many nesting level we have. But this tread of is acceptable. And yes this way state machines are holding references to outer state machines if it is running as a nested state machine and states which are nested state machines are holding references to inner state machine instances. Every state machine uses the same interface to communicate and we hide the construction of the state machines with factories. So this way the outer does not exposed to the inner the inner does not exposed to the outer leads us to a compile-able code :).
I personally use callbacks with boost::function or signals to avoid cycles but yes, it is still a logical cycle.
In case the state machine is in a stare where the sate is actually a holder of an inner state machine there is a Row with a baseEvent which this case re-dispatches the event to the inner one. Now if the inner one does not handles the event it shall send it back to the outer one. My assumption was that I can use the no_transition for this purpose but the implementation turns out different :(. For me name no_transition suggests that if no transition triggered. This function will be called and if the guard rejects the transition than this means to me that the transition is not triggered. Anyhow we could argue on it but in the end it does not help with my problem ....
Sure. I see other ways out of your problem. I think they are elegant and conform to the spirit of state machines: - exit points, as previously said. Just write another region as last, which will process your event and send it out to the outer. This has the disadvantage to force you to check is the event has been processed or not. - internal transitions in the submachine itself. IIUC, SubRunning is meant to be a submachine, right? Then we only need to add this to its definition:
struct internal_transition_table : mpl::vector< Internal < event3 , Action , Guard > > {};
(supposing you need a guard, this is optional). What does this mean? We added to your submachine (or substate) an internal transition table. By convention, MSM decides this is less "inner" than a standard transition table, so the transition table is tried first. If no transition from the transition table processes the event (or if the event is rejected by guards), then the internal table is tried. This can be easily used to replace your use of no_transition, it is more elegant, Standard-conforming and you have extra capabilities (like having different handlers, conflicting ones solved by guards, etc). I find this quite fun :)
The whole internal transitions have been rewritten in 1.48 to allow this to work for all machines so you will need it or trunk (sadly this also means that while I tested as well as I could, I cannot exclude the possibility of a bug).
HTH, Christophe
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

Hi Christophe Bad news I have tried to add an internal event table to my state base class that it is forwards the event to the outer one if the state machine where the state is used is a sub state machine. I have added a line something like this : struct internal_transition_table : boost::mpl::vector< // Event Action Guard // +-----------+------------------+---------------+ bMsmFr::Internal < BaseEvent , IntFwdToOuterFct , bMsmFr::none > // +-----------+------------------+---------------+ > {}; IntFwdToOuterFct check when ever the event can be forwarded to an outer state machine ( is there an outer state machine ) and if there is one it calls the interface function to process it. if there is none that prints a warning that the event is not handled in this state. before this change when I compiled the same state machine the compiler was using up 1.7 GB heap to compile this particular CPP file. Whit the change above I get a following error and a crash in a compiler when the heap usage reaches 4.2 GB \boost/mpl/for_each.hpp(69) : fatal error C1060: compiler is out of heap space I was using VC9 64bit version on win7 64bit with 12G memory ...... So by adding the internal transition table makes compiler heap usage to grow around by 300 % So I can not compile such a code.... Cheers Richie On 25 November 2011 02:54, Richard Szabo <sz.richard@googlemail.com> wrote:
Hi Christophe
Sure. I see other ways out of your problem. I think they are elegant and conform to the spirit of state machines: - exit points, as previously said. Just write another region as last, which will process your event and send it out to the outer. This has the disadvantage to force you to check is the event has been processed or not. - internal transitions in the submachine itself. IIUC, SubRunning is meant to be a submachine, right? Then we only need to add this to its definition:
struct internal_transition_table : mpl::vector< Internal < event3 , Action , Guard > > {};
(supposing you need a guard, this is optional). What does this mean? We added to your submachine (or substate) an internal transition table. By convention, MSM decides this is less "inner" than a standard transition table, so the transition table is tried first. If no transition from the transition table processes the event (or if the event is rejected by guards), then the internal table is tried. This can be easily used to replace your use of no_transition, it is more elegant, Standard-conforming and you have extra capabilities (like having different handlers, conflicting ones solved by guards, etc). I find this quite fun :)
Great Great Great .... you are always 1 step a head of me .... Yes this sunds a nice solution and even I can hide the internal transition table in ta sub state machine running base state all states creating sub states are just can use this base state .... This is cool I hope internal_transition_table allows to use baseEvents as well like outer ones ...
The whole internal transitions have been rewritten in 1.48 to allow this to work for all machines so you will need it or trunk (sadly this also means that while I tested as well as I could, I cannot exclude the possibility of a bug).
Don't worry I will come back to you if it is buggy ... I think with our virtual test system we will find problems quick if there any ...
Thanks for the support
Cheers Richie
On 24 November 2011 22:21, Christophe Henry <christophe.j.henry@googlemail.com> wrote:
Here is the execution output of the example attached :
MS1 Entering State: Idle by: event1 MS1 Leaving State: Idle by: event1 MS1 Entering State: SubRunning by: event1 FwdGuard: returns:1, event: event2 Processing BaseEvent Instance(event2) Setting guard false FwdGuard: returns:0, event: event3 MS1 DEFERED: event3 FwdGuard: returns:0, event: event4 FwdGuard: returns:0, event: event3 MS1 DEFERED: event3 FwdGuard: returns:0, event: event5 MS1 Leaving State: SubRunning by: event5 MS1 Entering State: AfterSub by: event5 MS1 no_transition event (event3)
It seems that the bug not fixed ...
Sorry, I should have been more explicit (and avid sheating to save a guard). You need to invert your 2 transitions to get the desired effect (processing is done from the bottom of the table):
Row < SubRunning , baseEvent , none , ProcessBaseEvent , FwdGuard >, Row < SubRunning , event3 , none , LogDefer , none >
Now, deferring has a higher priority, which gives the following desired output:
MS1 Entering State: Idle by: event1 MS1 Leaving State: Idle by: event1 MS1 Entering State: SubRunning by: event1 FwdGuard: returns:1, event: event2 Processing BaseEvent Instance(event2) Setting guard false MS1 DEFERED: event3 // ok, defer event3 FwdGuard: returns:0, event: event4 //event4 is not deferred MS1 DEFERED: event3 // event4 processed, can we process event3? Nope, defer again FwdGuard: returns:0, event: event5 MS1 Leaving State: SubRunning by: event5 MS1 Entering State: AfterSub by: event5 // we changed state, can we process event3? MS1 no_transition event (event3) // yes but we have no transition, call handler
but this is my least worries ( partly my fault You have asked me to test it for you .... but I did not sorry about that).
My bigger problem is the interpretation of no_transition you saying that it is an error situation... Well I have in my code a lot of situation when there are signals which are not interested to my state machine but they are dispatched to me anyhow our modules are used by different places for different purposes and there are signals which are useful in one case but not needed for my purpose I just simply ignore them these are ending up in no_transition so there I just write a log entry that signal is not handled so for me it is not an error at all it is the intended behavior.
State machines have a long history in the hardware world. And there it is tradition that if no transition processes an event, it is an error. Purists spend a lot of time handling every event in every state and even have state tables to check this. This means that to be completely correct, we should write an internal transition in every state for every event to document we choose to ignore these events (I try to do it regularly and it does help me from time to time). The UML Standard kept all this. When a transition could handle the event but rejects it, the event is processed. For once the Standard is about clear... ;-) But there are solutions to your problem in MSM's toolbox.
Now back to the My blocking problem .....
We use at the moment about 36 MSM state machines
:)
out of this 26 are performing specific well defined configuration task these 26 state-machine are nested to each in to each other to perform complicated behaviors sometimes there are nesting goes done to 5 level deep. We have, I call them main state machines which are connect these small task to each other to build complex behavior. We have more than 500 different row entries spitted in to these state machines. At the beginning we try to use MSM way of state machine nesting and soon we figured out that there is no compiler in the word which can compile/link this code.
Admittedly. We'll have to be patient.
Than I have decided to make smaller compile unit and make each state machine as a separate compile unit and let communicate them over a simple interface. Yes this slows done the dispatching because we have to re-dispatch each event in run-time as many times as many nesting level we have. But this tread of is acceptable. And yes this way state machines are holding references to outer state machines if it is running as a nested state machine and states which are nested state machines are holding references to inner state machine instances. Every state machine uses the same interface to communicate and we hide the construction of the state machines with factories. So this way the outer does not exposed to the inner the inner does not exposed to the outer leads us to a compile-able code :).
I personally use callbacks with boost::function or signals to avoid cycles but yes, it is still a logical cycle.
In case the state machine is in a stare where the sate is actually a holder of an inner state machine there is a Row with a baseEvent which this case re-dispatches the event to the inner one. Now if the inner one does not handles the event it shall send it back to the outer one. My assumption was that I can use the no_transition for this purpose but the implementation turns out different :(. For me name no_transition suggests that if no transition triggered. This function will be called and if the guard rejects the transition than this means to me that the transition is not triggered. Anyhow we could argue on it but in the end it does not help with my problem ....
Sure. I see other ways out of your problem. I think they are elegant and conform to the spirit of state machines: - exit points, as previously said. Just write another region as last, which will process your event and send it out to the outer. This has the disadvantage to force you to check is the event has been processed or not. - internal transitions in the submachine itself. IIUC, SubRunning is meant to be a submachine, right? Then we only need to add this to its definition:
struct internal_transition_table : mpl::vector< Internal < event3 , Action , Guard > > {};
(supposing you need a guard, this is optional). What does this mean? We added to your submachine (or substate) an internal transition table. By convention, MSM decides this is less "inner" than a standard transition table, so the transition table is tried first. If no transition from the transition table processes the event (or if the event is rejected by guards), then the internal table is tried. This can be easily used to replace your use of no_transition, it is more elegant, Standard-conforming and you have extra capabilities (like having different handlers, conflicting ones solved by guards, etc). I find this quite fun :)
The whole internal transitions have been rewritten in 1.48 to allow this to work for all machines so you will need it or trunk (sadly this also means that while I tested as well as I could, I cannot exclude the possibility of a bug).
HTH, Christophe
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

Hi Christophe
Bad news I have tried to add an internal event table to my state base class that it is forwards the event to the outer one if the state machine where the state is used is a sub state machine.
I have added a line something like this :
struct internal_transition_table : boost::mpl::vector< // Event Action Guard // +-----------+------------------+---------------+ bMsmFr::Internal < BaseEvent , IntFwdToOuterFct , bMsmFr::none > // +-----------+------------------+---------------+ > {};
IntFwdToOuterFct check when ever the event can be forwarded to an outer state machine ( is there an outer state machine ) and if there is one it calls the interface function to process it. if there is none that prints a warning that the event is not handled in this state.
Yes, should be correct.
before this change when I compiled the same state machine the compiler was using up 1.7 GB heap to compile this particular CPP file. Whit the change above I get a following error and a crash in a compiler when the heap usage reaches 4.2 GB
\boost/mpl/for_each.hpp(69) : fatal error C1060: compiler is out of heap space
I was using VC9 64bit version on win7 64bit with 12G memory ......
Usually, this doesn't help with VC9 :(
So by adding the internal transition table makes compiler heap usage to grow around by 300 %
So I can not compile such a code....
Cheers Richie
This is strange because it's not the kind of feature which typically hurts the compiler, so I suspect some of the regular VC9 "surprises". I tried with VC10 on some big state machine and the difference was almost none on heap and compile-time. If you could send me an example, I could play with it and see what can be done. In the meantime, we can try the following: - compile with VC10 to see if you see a difference in heap and compile-time with/without internal transitions. - if ok, we try the usual culprits: do you compile with /Gm- (minimal rebuild)? Don't. - is /Zm on? Usually we don't need it. You can also try with different values. Interestingly, usually, smaller values work better than large ones. Regards, Christophe
participants (2)
-
Christophe Henry
-
Richard Szabo