Обработка ошибок из каждого состояния в Boost MSM - PullRequest
1 голос
/ 20 мая 2019

Я использую Boost MSM для моделирования поведения робота. Существует несколько состояний, таких как «Ручное перемещение», «Автоматическое перемещение», «Работа», «В режиме ожидания» и т. Д. Однако мне нужно иметь возможность принудительно останавливать робота из любого состояния, переводя его в состояние, в котором он не может двигаться и не может получать новые команды, поэтому я создал состояние «EmergencyStopped». Когда робота просят перевооружиться, он должен вернуться в состояние «Холостой ход».

Однако Boost не рекомендует создавать переходы из одного состояния в одно и предпочитает использовать ортогональные состояния. Таким образом, я мог бы, например, сделать "AllOk" и interrupt_state "EmergencyStopped" ортогональные состояния.

Проблема в том, что, хотя я легко могу перевести робота в режим «EmergencyStopped», я не могу выйти из него и перевести робота в состояние «ожидания» из того состояния, в котором он был раньше. Например, если робот делает:

  • [Работа, AllOk] -> остановка
  • [Работа, EmergencyStopped] -> восстановить

Робот будет находиться в состоянии [Работа, AllOk], а я хочу, чтобы он перешел в состояние [Idle, AllOk].

Итак, мои вопросы:

  • Можно и нужно ли использовать ортогональные состояния для этого рабочего процесса? Если да, то как заставить состояние «простаивать» при выходе из ортогонального состояния «EmergencyStopped»?
  • Или я должен сделать «EmergencyStopped» неортогональным и объявить переход из всех состояний в него?
  • Или есть другое решение?

1 Ответ

2 голосов
/ 23 мая 2019

Есть другое решение. В вашем случае составное состояние - лучший выбор. Смотрите схему.

StateMachineDiagram

Как не писать много переходов в "EmergencyStopped".

Поместите все состояния, которые должны перейти в «EmergencyStopped», если событие «stop» произошло в состоянии «Normal». И поместите переход, который является от "Нормального" до "EmergencyStopped". Его триггерное событие - «стоп». Этот подход может избежать записи многих переходов в «EmergencyStopped». Даже если вы добавите другое child состояние «Normal», вам не нужно добавлять переход для добавленного состояния. Это одно из преимуществ подхода с составным состоянием.

Как перейти в состояние ожидания, если произошло событие восстановления.

Установить состояние "В режиме ожидания" на initial_state. Он отражает исходное состояние puseudo в диаграмме конечного автомата UML.

typedef mpl::vector<Idle> initial_state;

Если целью перехода является родительское состояние «Нормальное», то целевым состоянием перехода является состояние «Незанято», поскольку оно помечено как 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());
}

и запущенная демка https://wandbox.org/permlink/uBm6jTvG0YL3gSgl

...