处理 Boost MSM 中每个状态的错误
Handle an error from every state in Boost MSM
我正在使用 Boost MSM 来模拟机器人的行为。有多种状态,如"ManualMove"、"AutoMove"、"Work"、"Idle"等...
但是我需要能够从任何状态强制停止机器人,将其置于无法移动且无法接收新命令的状态,因此我创建了一个 "EmergencyStopped" 状态。
当机器人被要求重新武装时,机器人应该回到"Idle"状态。
但是,Boost 不建议创建从所有状态到单个状态的转换,而更喜欢使用正交状态。
例如,我可以做 "AllOk" 和 interrupt_state
"EmergencyStopped" 正交状态。
问题是,虽然我可以轻松地将机器人置于 "EmergencyStopped",但我无法退出它并使机器人从之前的状态进入 "Idle" 状态。例如,如果机器人执行:
- [工作,一切正常] -> 停止
- [工作,紧急停止] -> 恢复
机器人将处于 [Work, AllOk] 状态,而我希望它进入 [Idle, AllOk] 状态。
所以我的问题是:
- 我可以并且应该为此工作流程使用正交状态吗?如果是,当我退出正交状态"EmergencyStopped"时如何强制状态为"Idle"?
- 或者我应该使 "EmergencyStopped" 非正交并声明从所有状态到它的过渡吗?
- 或者有其他解决办法吗?
还有一个解决办法。在您的情况下,复合状态是更好的选择。见图。
如何避免将许多转换写入 "EmergencyStopped"。
Em-place 所有需要转换到 "EmergencyStopped" 的状态,如果 "stop" 事件发生到 "Normal" 状态。并放置从 "Normal" 到 "EmergencyStopped" 的过渡。它的触发事件是"stop"。这种方法可以避免将许多转换写入 "EmergencyStopped"。即使您要添加 "Normal" 的其他 child 状态,也不需要为添加的状态添加转换。这是复合状态方法的好处之一。
如果 "recover" 事件发生,如何转换到 "Idle" 状态。
将 "Idle" 状态设置为 initial_state
。它反映了 UML state-machine 图中的初始伪状态。
typedef mpl::vector<Idle> initial_state;
如果转换目标是父状态"Normal",那么转换目标状态是"Idle"状态,因为它被标记为initial_state
。
这两个技巧解决了你的问题。
完整代码如下:
#include <iostream>
#include <boost/msm/back/state_machine.hpp>
#include <boost/msm/front/state_machine_def.hpp>
#include <boost/msm/front/functor_row.hpp>
#include <boost/static_assert.hpp>
namespace msm = boost::msm;
namespace msmf = boost::msm::front;
namespace mpl = boost::mpl;
// ----- Events
struct ev1 {};
struct ev2 {};
struct ev3 {};
struct stop {};
struct recover {};
// ----- State machine
struct YourSystem_:msmf::state_machine_def<YourSystem_>
{
struct Normal_:msmf::state_machine_def<Normal_>
{
template <class Event,class Fsm>
void on_entry(Event const&, Fsm&) const {
std::cout << "Normal::on_entry()" << std::endl;
}
template <class Event,class Fsm>
void on_exit(Event const&, Fsm&) const {
std::cout << "Normal::on_exit()" << std::endl;
}
struct Idle:msmf::state<> {
template <class Event,class Fsm>
void on_entry(Event const&, Fsm&) const {
std::cout << "Idle::on_entry()" << std::endl;
}
template <class Event,class Fsm>
void on_exit(Event const&, Fsm&) const {
std::cout << "Idle::on_exit()" << std::endl;
}
};
struct Work:msmf::state<> {
template <class Event,class Fsm>
void on_entry(Event const&, Fsm&) const {
std::cout << "Work::on_entry()" << std::endl;
}
template <class Event,class Fsm>
void on_exit(Event const&, Fsm&) const {
std::cout << "Work::on_exit()" << std::endl;
}
};
struct AllOk:msmf::state<> {
template <class Event,class Fsm>
void on_entry(Event const&, Fsm&) const {
std::cout << "AllOk::on_entry()" << std::endl;
}
template <class Event,class Fsm>
void on_exit(Event const&, Fsm&) const {
std::cout << "AllOk::on_exit()" << std::endl;
}
};
// Set initial state
typedef mpl::vector<Idle> initial_state;
// Transition table
struct transition_table:mpl::vector<
// Start Event Next Action Guard
msmf::Row < Idle, ev1, Work, msmf::none, msmf::none >,
msmf::Row < Work, ev2, AllOk, msmf::none, msmf::none >,
msmf::Row < AllOk, ev3, Idle, msmf::none, msmf::none >
> {};
};
struct EmergencyStopped:msmf::state<>
{
template <class Event,class Fsm>
void on_entry(Event const&, Fsm&) const {
std::cout << "EmergencyStopped::on_entry()" << std::endl;
}
template <class Event,class Fsm>
void on_exit(Event const&, Fsm&) const {
std::cout << "EmergencyStopped::on_exit()" << std::endl;
}
};
typedef msm::back::state_machine<Normal_> Normal;
// Set initial state
typedef Normal initial_state;
// Transition table
struct transition_table:mpl::vector<
// Start Event Next Action Guard
msmf::Row < Normal, stop, EmergencyStopped, msmf::none, msmf::none >,
msmf::Row < EmergencyStopped, recover, Normal, msmf::none, msmf::none >
> {};
};
// Pick a back-end
typedef msm::back::state_machine<YourSystem_> Ys;
int main()
{
Ys ys;
ys.start();
std::cout << "> Send ev1()" << std::endl;
ys.process_event(ev1());
std::cout << "> Send ev2()" << std::endl;
ys.process_event(ev2());
std::cout << "> Send stop()" << std::endl;
ys.process_event(stop());
std::cout << "> Send recover()" << std::endl;
ys.process_event(recover());
}
我正在使用 Boost MSM 来模拟机器人的行为。有多种状态,如"ManualMove"、"AutoMove"、"Work"、"Idle"等... 但是我需要能够从任何状态强制停止机器人,将其置于无法移动且无法接收新命令的状态,因此我创建了一个 "EmergencyStopped" 状态。 当机器人被要求重新武装时,机器人应该回到"Idle"状态。
但是,Boost 不建议创建从所有状态到单个状态的转换,而更喜欢使用正交状态。
例如,我可以做 "AllOk" 和 interrupt_state
"EmergencyStopped" 正交状态。
问题是,虽然我可以轻松地将机器人置于 "EmergencyStopped",但我无法退出它并使机器人从之前的状态进入 "Idle" 状态。例如,如果机器人执行:
- [工作,一切正常] -> 停止
- [工作,紧急停止] -> 恢复
机器人将处于 [Work, AllOk] 状态,而我希望它进入 [Idle, AllOk] 状态。
所以我的问题是:
- 我可以并且应该为此工作流程使用正交状态吗?如果是,当我退出正交状态"EmergencyStopped"时如何强制状态为"Idle"?
- 或者我应该使 "EmergencyStopped" 非正交并声明从所有状态到它的过渡吗?
- 或者有其他解决办法吗?
还有一个解决办法。在您的情况下,复合状态是更好的选择。见图。
如何避免将许多转换写入 "EmergencyStopped"。
Em-place 所有需要转换到 "EmergencyStopped" 的状态,如果 "stop" 事件发生到 "Normal" 状态。并放置从 "Normal" 到 "EmergencyStopped" 的过渡。它的触发事件是"stop"。这种方法可以避免将许多转换写入 "EmergencyStopped"。即使您要添加 "Normal" 的其他 child 状态,也不需要为添加的状态添加转换。这是复合状态方法的好处之一。
如果 "recover" 事件发生,如何转换到 "Idle" 状态。
将 "Idle" 状态设置为 initial_state
。它反映了 UML state-machine 图中的初始伪状态。
typedef mpl::vector<Idle> initial_state;
如果转换目标是父状态"Normal",那么转换目标状态是"Idle"状态,因为它被标记为initial_state
。
这两个技巧解决了你的问题。
完整代码如下:
#include <iostream>
#include <boost/msm/back/state_machine.hpp>
#include <boost/msm/front/state_machine_def.hpp>
#include <boost/msm/front/functor_row.hpp>
#include <boost/static_assert.hpp>
namespace msm = boost::msm;
namespace msmf = boost::msm::front;
namespace mpl = boost::mpl;
// ----- Events
struct ev1 {};
struct ev2 {};
struct ev3 {};
struct stop {};
struct recover {};
// ----- State machine
struct YourSystem_:msmf::state_machine_def<YourSystem_>
{
struct Normal_:msmf::state_machine_def<Normal_>
{
template <class Event,class Fsm>
void on_entry(Event const&, Fsm&) const {
std::cout << "Normal::on_entry()" << std::endl;
}
template <class Event,class Fsm>
void on_exit(Event const&, Fsm&) const {
std::cout << "Normal::on_exit()" << std::endl;
}
struct Idle:msmf::state<> {
template <class Event,class Fsm>
void on_entry(Event const&, Fsm&) const {
std::cout << "Idle::on_entry()" << std::endl;
}
template <class Event,class Fsm>
void on_exit(Event const&, Fsm&) const {
std::cout << "Idle::on_exit()" << std::endl;
}
};
struct Work:msmf::state<> {
template <class Event,class Fsm>
void on_entry(Event const&, Fsm&) const {
std::cout << "Work::on_entry()" << std::endl;
}
template <class Event,class Fsm>
void on_exit(Event const&, Fsm&) const {
std::cout << "Work::on_exit()" << std::endl;
}
};
struct AllOk:msmf::state<> {
template <class Event,class Fsm>
void on_entry(Event const&, Fsm&) const {
std::cout << "AllOk::on_entry()" << std::endl;
}
template <class Event,class Fsm>
void on_exit(Event const&, Fsm&) const {
std::cout << "AllOk::on_exit()" << std::endl;
}
};
// Set initial state
typedef mpl::vector<Idle> initial_state;
// Transition table
struct transition_table:mpl::vector<
// Start Event Next Action Guard
msmf::Row < Idle, ev1, Work, msmf::none, msmf::none >,
msmf::Row < Work, ev2, AllOk, msmf::none, msmf::none >,
msmf::Row < AllOk, ev3, Idle, msmf::none, msmf::none >
> {};
};
struct EmergencyStopped:msmf::state<>
{
template <class Event,class Fsm>
void on_entry(Event const&, Fsm&) const {
std::cout << "EmergencyStopped::on_entry()" << std::endl;
}
template <class Event,class Fsm>
void on_exit(Event const&, Fsm&) const {
std::cout << "EmergencyStopped::on_exit()" << std::endl;
}
};
typedef msm::back::state_machine<Normal_> Normal;
// Set initial state
typedef Normal initial_state;
// Transition table
struct transition_table:mpl::vector<
// Start Event Next Action Guard
msmf::Row < Normal, stop, EmergencyStopped, msmf::none, msmf::none >,
msmf::Row < EmergencyStopped, recover, Normal, msmf::none, msmf::none >
> {};
};
// Pick a back-end
typedef msm::back::state_machine<YourSystem_> Ys;
int main()
{
Ys ys;
ys.start();
std::cout << "> Send ev1()" << std::endl;
ys.process_event(ev1());
std::cout << "> Send ev2()" << std::endl;
ys.process_event(ev2());
std::cout << "> Send stop()" << std::endl;
ys.process_event(stop());
std::cout << "> Send recover()" << std::endl;
ys.process_event(recover());
}