Boost MSM: Can not get out of the submachine

Hi,
I have been playing around with the boost MSM libraries, to know more about
them. I am creating a vending machine simulation with a sub-machine for
accepting payment from the user. The vending machine has 3 states: waiting
(where the machine just waits for the user to start processing), selection
(where user selects a product. The machine makes a transition to this state
from "waiting" state if the event "key_press" occurs and if the key pressed
is the start button (checked by a guard condition).). After a product is
selected the machine goes in payment state, which is a submachine that
accepts payment in quarters. After the payment is received (it is fixed $1
for all products, just to make it simple) the machine again goes in the
waiting state dispensing the product (action: dispatch_item).
The submachine Payment has 4 states: Payment_init which just displays a
message and then makes an immediate automatic transition to the next state
"Paying". In this state the machine just waits for the "coin_insert" event
to occur (which represents inserting a quarter). Once the "coin_insert"
event occurs it makes a transition to "check_payment" state which checks if
sufficient amount was paid. If not it again makes an automatic transition to
"Paying" state, else it moves to the "Payment_done" state which is the last
state in the submachine. This state generates an event "last_state" which is
an indicator that the payment has been made and should cause a transition
from the submachine "Payment" to waiting state (which I am not observing).
The transition tables for both the machines are:
(btw I am using the functors as the front end.)
Table for vending machine:
//
+---------------------+-----------------------+----------------------+---------------------+-------
Row < waiting, key_press, selection, none, is_start>, /// If start button
pressed, go to selection state
Row < waiting, key_press, waiting, none, is_cancel>, // If cancel pressed
stay in the waiting state
Row < selection, key_press, Payment, none, is_number>, //If a product
number pressed go to payment
Row < selection, key_press, waiting, none, is_cancel>, // If cancel
pressed go to
Row < Payment, key_press, waiting, none, is_cancel>,
Row < Payment, last_state, waiting, dispatch_item, none> //the last_state
event is generated in the
//
"payment_done" state of the Payment submachine
//
+---------+-------------+---------+---------------------+----------------------+
Table for Payment machine:
//
+---------------------+-----------------------+----------------------+---------------------+-------
Row < Payment_init, none, Paying, none, none >, // Just make an auto
transition after displaying a msg
Row < Paying, coin_insert, check_payment, none, none>,
Row < check_payment, none, Paying, none, is_not_paid>, //If enough
money received
Row < check_payment, none, payment_done, none, is_paid>
//
+---------+-------------+---------+---------------------+----------------------+
The code for payment_done state is:
struct payment_done: public msm::front::state
{
template

Hi,
I have been playing around with the boost MSM libraries, to know more about them. I am creating a vending machine simulation with a sub-machine for accepting payment from the user. The vending machine has 3 states: waiting (where the machine just waits for the user to start processing), selection (where user selects a product. The machine makes a transition to this state from "waiting" state if the event "key_press" occurs and if the key pressed is the start button (checked by a guard condition).). After a product is selected the machine goes in payment state, which is a submachine that accepts payment in quarters. After the payment is received (it is fixed $1 for all products, just to make it simple) the machine again goes in the waiting state dispensing the product (action: dispatch_item).
The submachine Payment has 4 states: Payment_init which just displays a message and then makes an immediate automatic transition to the next state "Paying". In this state the machine just waits for the "coin_insert" event to occur (which represents inserting a quarter). Once the "coin_insert" event occurs it makes a transition to "check_payment" state which checks if sufficient amount was paid. If not it again makes an automatic transition to "Paying" state, else it moves to the "Payment_done" state which is the last state in the submachine. This state generates an event "last_state" which is an indicator that the payment has been made and should cause a transition from the submachine "Payment" to waiting state (which I am not observing). The transition tables for both the machines are: (btw I am using the functors as the front end.)
<snip>
The code for payment_done state is: struct payment_done: public msm::front::state { template
void on_entry(Event const &, FSM &fsm) { cout << "---------------" << endl; cout << "Payment_done" << endl; fsm.process_event(last_state()); } };
The FSM template argument, for a given state, is the state machine immediately containing the state, in your case, Payment, which cannot process this event. Later, I plan to provide you with more (although I have mixed feelings to this feature), at best the list of all state machines in the path to your state. There are several ways to solve this, but the UML "standard" way is to make payment_done an exit pseudo state (msm::front::exit_pseudo_state).
As you can see the event "last_state" is occurring in the payment_done state but the transition is not taking place. I can post the entire code is someone is interested but I am just playing around with the libraries so the code (and some logic too) is not sophisticated at all. Still I can post it if someone wants to take a look at it.
Including a minimum working code is always helpful and would help me try your example out, so in general, yes, it is a good idea. Regards, Christophe

Thank you for the response. I have moved generation of the event "last_state"
to main function instead of the payment_done struct (I have attached the
code too. As I mentioned the code and some logic is not sophisticated.) and
the code seems to be working. However if I want to make an auto-transition
from the Payment sub-machine (once it reaches the final "payment_done"
state) to the next state in the vending machine how do I do that? I tried to
declare en exit_pseudo_state without an event (struct payment_done: public
exit_pseudo_state

Thank you for the response. I have moved generation of the event "last_state" to main function instead of the payment_done struct (I have attached the code too. As I mentioned the code and some logic is not sophisticated.) and the code seems to be working.
ok
However if I want to make an auto-transition from the Payment sub-machine (once it reaches the final "payment_done" state) to the next state in the vending machine how do I do that? I tried to declare en exit_pseudo_state without an event (struct payment_done: public exit_pseudo_state
) and then declare an auto-transition from "Payment" submachine to "waiting" state in the vending machine (Row < Payment::exit_pt , none, waiting, dispense_item, none>) but I am receiving compilation errors.
I tried and it worked. I did:
- declare pseudo exit: struct payment_done: public exit_pseudo_state<none>
- change inside transition: Row < check_payment, none, payment_done, none,
is_paid>
- changed outside transition: Row < Payment::exit_pt
I have another question to ask. If one wants to access info stored in one state from another state, what will be an elegant way to do that? For eg. In case of the vending machine example, lets assume the selection state stores the product selected by the customer. This info then needs to be accessed while dispensing the product at later stages. One way could be storing pointers to all the instances of structs that represent state in the state machine. However in this case submachines may not be able to access the info stored in the parent machine.
An elegant way to do this is to add this info in the event. Like this you can save it in Payment (you get the event in on_entry and the target state is also passed to the transition). I like to see this as the UML equivalent of a function call. Christophe

Christophe Henry-3 wrote:
I tried and it worked. I did: - declare pseudo exit: struct payment_done: public exit_pseudo_state<none> - change inside transition: Row < check_payment, none, payment_done, none, is_paid> - changed outside transition: Row < Payment::exit_pt
, none, waiting, dispense_item, none> And it compiles and does the work (from what I understood of your program). I tried with the newest version. If you still have a problem, it might be an old bug. What version are you using?
--> I got it working too. I was defining the payment_done state as struct payment_done: public exit_pseudo_state<>, instead of struct payment_done: public exit_pseudo_state<none>. I corrected it and now the code is compiling without error. Christophe Henry-3 wrote:
An elegant way to do this is to add this info in the event. Like this you can save it in Payment (you get the event in on_entry and the target state is also passed to the transition). I like to see this as the UML equivalent of a function call.
--> As per my understanding, it will be possible to convey the info through an event only if two states are "connected" by an event. But if one has to access info in one state from another state and if they are not connected by an event as such, what will be the best way to do that? (In the vending machine example, the product gets selected in the "selection" state and gets dispensed in the dispense_item action. These two are not directly connected through an event.) I can think of one way (not sure if its the best way to do that though) where the state machine itself stores pointers to objects that represent states. (eg: state machine have a pointer struct selection *sel_ptr; The selection state on entry will then update this variable with "this" ptr. i.e the on_entry method of selection state will have a statement: sel_prt = this;). This won't however work if a submachine has to access info stored in the parent machine. Thanks! -- View this message in context: http://boost.2283326.n4.nabble.com/Boost-MSM-Can-not-get-out-of-the-submachi... Sent from the Boost - Users mailing list archive at Nabble.com.

I tried and it worked. I did: - declare pseudo exit: struct payment_done: public exit_pseudo_state<none> - change inside transition: Row < check_payment, none, payment_done, none, is_paid> - changed outside transition: Row < Payment::exit_pt
, none, waiting, dispense_item, none> And it compiles and does the work (from what I understood of your program). I tried with the newest version. If you still have a problem, it might be an old bug. What version are you using?
--> I got it working too. I was defining the payment_done state as struct payment_done: public exit_pseudo_state<>, instead of struct payment_done: public exit_pseudo_state<none>. I corrected it and now the code is compiling without error.
Ok, good.
An elegant way to do this is to add this info in the event. Like this you can save it in Payment (you get the event in on_entry and the target state is also passed to the transition). I like to see this as the UML equivalent of a function call.
--> As per my understanding, it will be possible to convey the info through an event only if two states are "connected" by an event. But if one has to access info in one state from another state and if they are not connected by an event as such, what will be the best way to do that? (In the vending machine example, the product gets selected in the "selection" state and gets dispensed in the dispense_item action. These two are not directly connected through an event.) I can think of one way (not sure if its the best way to do that though) where the state machine itself stores pointers to objects that represent states. (eg: state machine have a pointer struct selection *sel_ptr; The selection state on entry will then update this variable with "this" ptr. i.e the on_entry method of selection state will have a statement: sel_prt = this;). This won't however work if a submachine has to access info stored in the parent machine.
Thanks!
In your case, I think it'd make sense to pass it in the event going from
selection to Payment because the choice is the result of selection, right?
Then Payment could store it (or a reference/pointer to it) in its on_entry.
A second possibility (but IMO less clean) solution would be to add an action
in the transition selection - key_press - Payment. This action would set the
info into the target state (Payment). Then your submachine works with
correct data.
The third possibility (we're getting less and less clean here) is for
on_entry of Payment to access the info from its parent state machine (the
FSM parameter, as Payment is a substate of your vending_machine).
And finally, the method to use when nothing else works, get vending_machine
or selection set the data directly inside Payment (using
get_state
participants (2)
-
Christophe Henry
-
gauravk