I'm using boost::msm for state management in a robotics application. I have an outer state machine that controls the main flow and includes states such as Idle, Operating, and Error. The Operating state itself is an inner state machine with multiple sub-states.
I have a method in the outer state machine that sets a context object (TrajectoryManagerComponent* m_component). This object needs to be accessed by the inner state machine so that actions within the sub-states can call its methods.
The Challenge: I'm unable to pass the context directly to the constructor of the inner state machine because I don't instantiate the inner state machine directly—it is managed by boost::msm. Here's a simplified version of my current code:
struct ComponentStateMachine_ : public msm::front::state_machine_def<ComponentStateMachine_>
{
public:
TrajectoryManagerComponent* m_component;
void link_component(hyro::guidance::TrajectoryManagerComponent* component) { m_component = component; }
struct Operating_ : public msm::front::state_machine_def<Operating_>
{
template <typename Event, typename SM>
void on_entry(Event const&, SM& fsm)
{
m_logger->info("Accessing component, we might now use its methods. Component is {}", **fsm.m_component**);
What is the best way to access the context (m_component) from the outer state machine within the inner state machine's actions or states? I thought about passing it via a constructor, but since the inner state machine is created by boost::msm, I don't have direct control over its instantiation.
Is there a way to achieve this in Boost.MSM, or is there an alternative pattern that would allow me to access the context from the inner state machine?
Reading through the documentation I got the answer for this. As its written there:
Another possible use of the entry action is to pass data to substates / submachines. Launching is a substate containing a data attribute:
struct launcher_ : public msm::front::state_machine_def<launcher_>{
Data current_calculation;
// state machines also have entry/exit actions
template <class Event, class Fsm>
void on_entry(Event const& evt, Fsm& fsm)
{
launcher_::Launching& s = fsm.get_state<launcher_::Launching&>();
s.data = fsm.current_calculation;
}
...
};
So, what I got to do in my case is to get the sub-state machine in the state machine on_entry method and set its .m_component
value to the value that is at the state machine var, like this:
struct ComponentStateMachine_ : public msm::front::state_machine_def<TrajectoryManagerStateMachine_>
{
public:
TrajectoryManagerComponent* m_component;
template <typename Event, typename FSM>
void on_entry(Event const&, FSM& fsm)
{
m_logger->info("Starting the state machine");
// getting a reference to the operating submachine
ComponentStateMachine_::Operating& submachine
= fsm.template get_state<TrajectoryManagerStateMachine_::Operating&>();
submachine.m_component = m_component;
}
...
struct Operating_ : public msm::front::state_machine_def<Operating_>
{
TrajectoryManagerComponent* m_component;
...