[statechart] Event handlers and state reusage

Hi, couple of quick questions: 1. For a state, is it possible to specify a method that will be called when an event with no defined handlers is received? 2. Is is somehow possible to reuse state definitions as substates of different "higher-level" states? Sorry about the terminology, I'll give a specific example: - I've got an application that can be either in remote or locally controlled mode (this is the highest-level states). - Certain events are allowed both in remote and local, while some are not. As an example, the "CommandEvent" event should be handled both in remote and local - resulting in the "ExecutingCommandState" being entered. - I'd like to be able to reuse the definition of ExecutingCommandState regardless of the enclosing state being Remote or Local - Possible? // Johan

Johan Nilsson wrote:
1. For a state, is it possible to specify a method that will be called when an event with no defined handlers is received?
Interesting question. Can all events inherit from a common base class, to which a state could define a (default) reaction, and override this with reactions to specific event sub-classes?
2. Is is somehow possible to reuse state definitions as substates of different "higher-level" states?
In boost/libs/statechart/doc/tutorial.html#TransitionActions it says, "With Boost.Statechart, a transition action can be a member of any common outer context" and goes on to give some examples. If I've understood your question correctly then the answer is "Yes".
- I've got an application that can be either in remote or locally controlled mode (this is the highest-level states). - Certain events are allowed both in remote and local, while some are not. As an example, the "CommandEvent" event should be handled both in remote and local - resulting in the "ExecutingCommandState" being entered. - I'd like to be able to reuse the definition of ExecutingCommandState regardless of the enclosing state being Remote or Local - Possible?
Could you implement this with orthogonal regions? Top Remote Local ------ Idle Executing Here the top state contains two orthogonal regions separated by a dashed line. The only problem here is that actions (in transitions from say Idle) that need to be different depending on whether it's in Remote or Local gain conditional logic. (If that's all Remote and Local are used for they could just be replaced by a bool member of Top.) If you had Top Remote RIdle RExecuting Local LIdle LExecuting you'd avoid this, at the expense of possibly repeating code in the (only slightly different) actions of a transition originating from either RIdle or LIdle. This common code could perhaps be extracted into a method belonging to the shared super-state, Top. This sounds like a similar problem to the one I face. Where are you planning to execute the command? In my application the commands take a long time, so are not suitable for implementing as an action. UML provides "Do Activities" that can be used for this purpose. The statechart docs suggest simulating this "with a separate thread that is started in the entry action and canceled (!) in the exit action of a particular state". I quite like that approach. Yours, Tim Milward

Tim Milward wrote:
Johan Nilsson wrote:
1. For a state, is it possible to specify a method that will be called when an event with no defined handlers is received?
Interesting question. Can all events inherit from a common base class, to which a state could define a (default) reaction, and override this with reactions to specific event sub-classes?
Should be possible from my point of view. Not sure how the framework would handle that even though it sounds reasonable. It's a bit limiting though, and should be possible have support for in the library. I'd figure it's a pretty common requirement to be able to take actions (log a warning or similar) when an unexpected event is received.
2. Is is somehow possible to reuse state definitions as substates of different "higher-level" states?
In boost/libs/statechart/doc/tutorial.html#TransitionActions it says, "With Boost.Statechart, a transition action can be a member of any common outer context" and goes on to give some examples. If I've understood your question correctly then the answer is "Yes".
Actually, I'm inclined to say "No" considering the following (partially duplicated from your part later in the posting): Top Remote Idle Executing Local Idle Executing This I cannot do, right? Idle and Executing would ideally be the same, but as they are statically associated with their outer states (and vice versa) it doesn't look like it's possible.
- I've got an application that can be either in remote or locally controlled mode (this is the highest-level states). - Certain events are allowed both in remote and local, while some are not. As an example, the "CommandEvent" event should be handled both in remote and local - resulting in the "ExecutingCommandState" being entered. - I'd like to be able to reuse the definition of ExecutingCommandState regardless of the enclosing state being Remote or Local - Possible?
Could you implement this with orthogonal regions?
Top Remote Local ------ Idle Executing
Here the top state contains two orthogonal regions separated by a dashed line.
Sorry for my ignorance, but what then happens if there would be an additional substate, that should only be possible when in e.g. the Remote state.
The only problem here is that actions (in transitions from say Idle) that need to be different depending on whether it's in Remote or Local gain conditional logic. (If that's all Remote and Local are used for they could just be replaced by a bool member of Top.) If you had
Top Remote RIdle RExecuting Local LIdle LExecuting
you'd avoid this, at the expense of possibly repeating code in the (only slightly different) actions of a transition originating from either RIdle or LIdle. This common code could perhaps be extracted into a method belonging to the shared super-state, Top.
This is more similar to what I've got. There isn't really any other code duplication that having to define the e.g. RIdle, LIdle, RExecuting, LExecuting, as the state machine itself holds a reference to the domain object responsible for executing the actions. Within the different states I simply get this object using context<StateMachine> and delegate - so the state machine is in this case only used for discriminating the disallowed events according to the current state. If I had lots of different actions that could be performed this would result in a very bloated interface for the domain object, but in this case it's not a problem - I can keep the interface down to 3-4 methods.
This sounds like a similar problem to the one I face. Where are you planning to execute the command? In my application the commands take a long time, so are not suitable for implementing as an action.
I'm planning to execute the command(s) in context of the react(...) method calls. Some of them do take a pretty long time to execute, but as the intention is to only accept one active command at a time it doesn't really matter (this isn't ideal though, see below).
UML provides "Do Activities" that can be used for this purpose. The statechart docs suggest simulating this "with a separate thread that is started in the entry action and canceled (!) in the exit action of a particular state". I quite like that approach.
I considered executing the commands in a worker thread similar to that approach; when receiving a command the FSM would enter the ExecutingCommand state, kick off a new thread (or using a previously existing worker thread) to execute the command. The problem I ran into that I wanted the command-executing thread _itself_ being able to signal the completion of the command (and thus trigger exiting the ExecutingCommand state to the previous state). It should never be possible to exit the state unless the command has run to an end, e.g. by unconditionally joining the worker thread in the state's destructor. I couldn't really find a clean, thread-safe way to implement this. Did you actually try to implement anything like this? Thanks // Johan

2. Is is somehow possible to reuse state definitions as substates of different "higher-level" states?
In boost/libs/statechart/doc/tutorial.html#TransitionActions it says, "With Boost.Statechart, a transition action can be a member of any common outer context" and goes on to give some examples. If I've understood your question correctly then the answer is "Yes".
Actually, I'm inclined to say "No" considering the following (partially duplicated from your part later in the posting):
Top Remote Idle Executing Local Idle Executing
This I cannot do, right? Idle and Executing would ideally be the same, but as they are statically associated with their outer states (and vice versa) it doesn't look like it's possible.
OK, I've miss understood. Does this help? boost/libs/statechart/doc/tutorial.html#SubmachinesAndParametrizedStates It describes how to use templates to implement submachines. In the above example you'd use Executing<Remote> and Executing<Local>.
- I've got an application that can be either in remote or locally controlled mode (this is the highest-level states). - Certain events are allowed both in remote and local, while some are not. As an example, the "CommandEvent" event should be handled both in remote and local - resulting in the "ExecutingCommandState" being entered. - I'd like to be able to reuse the definition of ExecutingCommandState regardless of the enclosing state being Remote or Local - Possible?
Could you implement this with orthogonal regions?
Top Remote Local ------ Idle Executing
Sorry for my ignorance, but what then happens if there would be an additional substate, that should only be possible when in e.g. the Remote state.
Then it would go in the remote state. If you want a state only available in Remote and Executing that can't be done. But then you are not simply reusing Executing submachine because it's different in Remote and Local.
Top Remote RIdle RExecuting Local LIdle LExecuting
This is more similar to what I've got. There isn't really any other code duplication that having to define the e.g. RIdle, LIdle, RExecuting, LExecuting, as the state machine itself holds a reference to the domain object responsible for executing the actions. Within the different states I simply get this object using context<StateMachine> and delegate - so the state machine is in this case only used for discriminating the disallowed events according to the current state.
So what is it you want to reuse? Is it a common list of reactions, to which each of L & RExecuting add the reactions specific to Local and Remote?
UML provides "Do Activities" that can be used for this purpose. The statechart docs suggest simulating this "with a separate thread that is started in the entry action and canceled (!) in the exit action of a particular state". I quite like that approach.
I considered executing the commands in a worker thread similar to that approach; when receiving a command the FSM would enter the ExecutingCommand state, kick off a new thread (or using a previously existing worker thread) to execute the command. The problem I ran into that I wanted the command-executing thread _itself_ being able to signal the completion of the command (and thus trigger exiting the ExecutingCommand state to the previous state). It should never be possible to exit the state unless the command has run to an end, e.g. by unconditionally joining the worker thread in the state's destructor.
I couldn't really find a clean, thread-safe way to implement this. Did you actually try to implement anything like this?
Not yet. Can the worker thread, after processing the command push a Finished event onto the queue. When the statemachine thread receives this event it exits the Executing state.

Tim, thanks for your earlier replies. I've been off the list for a while and couldn't respond earlier. Tim Milward wrote:
2. Is is somehow possible to reuse state definitions as substates of different "higher-level" states?
In boost/libs/statechart/doc/tutorial.html#TransitionActions it says, "With Boost.Statechart, a transition action can be a member of any common outer context" and goes on to give some examples. If I've understood your question correctly then the answer is "Yes".
Actually, I'm inclined to say "No" considering the following (partially duplicated from your part later in the posting):
Top Remote Idle Executing Local Idle Executing
This I cannot do, right? Idle and Executing would ideally be the same, but as they are statically associated with their outer states (and vice versa) it doesn't look like it's possible.
OK, I've miss understood. Does this help? boost/libs/statechart/doc/tutorial.html#SubmachinesAndParametrizedStates
It describes how to use templates to implement submachines. In the above example you'd use Executing<Remote> and Executing<Local>.
I tried this originally, but it somehow grew the complexity beyond my control so I reverted to my previous solution. IIRC it was rather complex to keep the forward declarations and actual definitions in the correct order when the template parameter was a kind of State. I might take another stab at it later. [snip]
Top Remote RIdle RExecuting Local LIdle LExecuting
This is more similar to what I've got. There isn't really any other code duplication that having to define the e.g. RIdle, LIdle, RExecuting, LExecuting, as the state machine itself holds a reference to the domain object responsible for executing the actions. Within the different states I simply get this object using context<StateMachine> and delegate - so the state machine is in this case only used for discriminating the disallowed events according to the current state.
So what is it you want to reuse? Is it a common list of reactions, to which each of L & RExecuting add the reactions specific to Local and Remote?
I did want to reuse the inner states - but in the end I found it wasn't worth the work involved, so I allowed myself to do some code duplication instead (which is basically what I said above - sorry for not being very clear).
UML provides "Do Activities" that can be used for this purpose. The statechart docs suggest simulating this "with a separate thread that is started in the entry action and canceled (!) in the exit action of a particular state". I quite like that approach.
I considered executing the commands in a worker thread similar to that approach; when receiving a command the FSM would enter the ExecutingCommand state, kick off a new thread (or using a previously existing worker thread) to execute the command. The problem I ran into that I wanted the command-executing thread _itself_ being able to signal the completion of the command (and thus trigger exiting the ExecutingCommand state to the previous state). It should never be possible to exit the state unless the command has run to an end, e.g. by unconditionally joining the worker thread in the state's destructor.
I couldn't really find a clean, thread-safe way to implement this. Did you actually try to implement anything like this?
Not yet. Can the worker thread, after processing the command push a Finished event onto the queue. When the statemachine thread receives this event it exits the Executing state.
I tried modeling this with an asynchronous statemachine and it looks promising. There's just one thing that I don't like - it doesn't seem to be possible to query the asynchronous statemachine for its current state. I realize that during real execution this isn't a good thing to do, but I need this for my unit tests. In the tests I feed the statemachine with events in a controlled environment and check that the transitions and the states' behaviour are as expected. // Johan

Hi Johan Sorry for the delay, I'm currently on holiday.
1. For a state, is it possible to specify a method that will be called when an event with no defined handlers is received?
You can do that globally for the whole state machine with the technique outlined by Oliver (thanks, Oliver!), or locally for one state or a group of states by defining a reaction that is triggered by sc::event_base rather than a concrete event. Such a reaction is usually the last one in the reactions typedef.
2. Is is somehow possible to reuse state definitions as substates of different "higher-level" states? Sorry about the terminology, I'll give a specific example:
- I've got an application that can be either in remote or locally controlled mode (this is the highest-level states). - Certain events are allowed both in remote and local, while some are not. As an example, the "CommandEvent" event should be handled both in remote and local - resulting in the "ExecutingCommandState" being entered. - I'd like to be able to reuse the definition of ExecutingCommandState regardless of the enclosing state being Remote or Local - Possible?
It seems Tim is right on the spot on this one (thanks, Tim!). Please let us know whether that works for you. HTH & Regards, Andreas

Andreas, Andreas Huber wrote:
Hi Johan
Sorry for the delay, I'm currently on holiday.
No need to apologize, we all need holidays.
1. For a state, is it possible to specify a method that will be called when an event with no defined handlers is received?
You can do that globally for the whole state machine with the technique outlined by Oliver (thanks, Oliver!), or locally for one state or a group of states by defining a reaction that is triggered by sc::event_base rather than a concrete event. Such a reaction is usually the last one in the reactions typedef.
Ok, thanks for the further description. It would help though, if the unconsumed_event method was documented explicitly in the docs. I think I missed it as it's not presented as a link - in the state_machine reference there's only a link to the description of process_event() effects in the line above. [snip] Thanks // Johan

Johan Nilsson wrote:
Ok, thanks for the further description. It would help though, if the unconsumed_event method was documented explicitly in the docs.
Fixed in CVS (trunk & 1.34 branch). Feedback welcome. Regards, -- Andreas Huber When replying by private email, please remove the words spam and trap from the address shown in the header.

Ok, thanks for the further description. It would help though, if the unconsumed_event method was documented explicitly in the docs.
Fixed in CVS (trunk & 1.34 branch). Feedback welcome.
I'm afraid that I just bother the group with this question: anonimous CVS doesn't seem to work for several weeks... :( With best regards, Serge Skorokhodov. E-mail: serge.skorokhodov@tochka.ru

Andreas Huber wrote:
Johan Nilsson wrote:
Ok, thanks for the further description. It would help though, if the unconsumed_event method was documented explicitly in the docs.
Fixed in CVS (trunk & 1.34 branch). Feedback welcome.
So, how can I get a preview - the anonymous cvs web interface doesn't seem to be working? // Johan

Johan Nilsson wrote:
Andreas Huber wrote:
Johan Nilsson wrote:
Ok, thanks for the further description. It would help though, if the unconsumed_event method was documented explicitly in the docs.
Fixed in CVS (trunk & 1.34 branch). Feedback welcome.
So, how can I get a preview - the anonymous cvs web interface doesn't seem to be working?
Ok, found it via the sourceforge.net links (the getting started doc at boost.org points to an incorrect location). Changes look good to me. Thanks / Johan
participants (4)
-
Andreas Huber
-
Johan Nilsson
-
Serge Skorokhodov
-
Tim Milward