[MSM] a timer/timeout for the whole state machine (including submachines)
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...
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
Thanks for your fast reply! Yes, you could also use a region with 1 state and internal transitions but
it's about the same.
"As much as possible in transition table" is one idea I got from you! ;-)
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.
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!
There is no timer in MSM for 2 reasons: - it's out of the scope of the library
just this reason seems enought to me! ;-)
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
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! ;-)
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
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... In your first answer you said:
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 fix I understand that events are not upper-forwarded:
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
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
Tai,
On Mon, Apr 9, 2012 at 8:11 AM, 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
Hi Albert,
I've tried the pseudo-exit solution you've proposed and it worked as you explain, thanks! But, I'm still a bit confused...
In your first answer you said:
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.
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.
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 fix I understand that events are not upper-forwarded:
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.
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.
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 :(
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
Hi!
There is no upper forwarding,
Ok! Now it is totally clear to me! ;-)
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)...
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!! ;-)
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!
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!! :-)
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...)
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
Hi Albert,
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.
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.
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?
Sounds like a lot of work ;-)
- 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?
- 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.
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?
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.
(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
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! :-) >> - A Timer inside all my (sub)state machines? > Sounds like a lot of work ;-) agree! ;-) >> - 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!). >> - 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... > 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. 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...? >> 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
>> 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. >>> - 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". > 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. > 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. > 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). >>> 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. > 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). > 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. > 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... > ;-) 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
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
participants (4)
-
Albert Gil Moreno
-
Chris Cleeland
-
Christophe Henry
-
Nguyen, Tai