Boost Statechart - Локальные переходы

Я надеюсь, что кто-то может помочь мне с этой проблемой, или, по крайней мере, указать на ошибку моих путей...

В качестве простой иллюстрации моей проблемы рассмотрим часть приложения, в которой вы можете войти в режим работы "Режим функций". Затем доступны четыре подрежима в зависимости от того, какую функциональную клавишу F1-F4 нажимает пользователь. По умолчанию вводится режим F1. Диаграмма состояний начинается следующим образом:

Диаграмма 1

Пользователь может в любой момент нажать F1-F4, чтобы переключиться в соответствующий режим. Добавление этих переходов во внутренние состояния приводит к следующему:

Диаграмма 2

Очевидно, что это (а) беспорядок, и (б) много переходов, чтобы определить. Если в какой-то момент я хочу добавить F5Mode, тогда... ну, вы получите картину. Чтобы избежать этого, я хотел бы сделать следующее:

Диаграмма 3

Диаграмма Boost State позволяет мне определять переходы из FunctionMode в любое из внутренних состояний, но результат не тот, который я ожидал. Фактический результат выглядит следующим образом:

Диаграмма 4

Т.е. нажатие F1-F4 для переключения режимов приводит к выходу из внешнего состояния FunctionMode и его повторному вводу наряду с запуском нежелательных действий выхода и входа.

Еще в 2006 году эта тема между автором библиотеки и пользователем описывала ту же проблему. Я думаю, что автор предлагает сделать следующее в качестве обходного пути:

Диаграмма 5

Тем не менее, этот обходной путь не кажется мне очень привлекательным: он добавил дополнительный уровень состояния для компиляции, код стал менее читабельным, пришлось бы использовать глубокую историю, чтобы вернуться к любому из подпрограмм режима работы. -состояния и объект Промежуточного состояния без необходимости разрушаются и строятся заново.

Итак... где я иду не так? Или каковы альтернативы? Я кратко рассмотрел Boost Meta State Machine (msm), но из того, что я видел до сих пор, я предпочитаю внешний вид Statechart.

Я удивлен, что больше пользователей не столкнулись с той же проблемой... что заставляет меня думать, что, возможно, мой подход совершенно неверен!

1 ответ

Решение

Вы смотрели на реакцию в состоянии, объясненную в руководстве по диаграмме состояний? Кажется, он делает то, что вы ищете.

Поскольку вы запрашиваете альтернативы, в этот период я ​​оцениваю различные реализации диаграмм состояний Care Harel. Я посмотрел на диаграмму состояний Boost и MSM Boost. Я написал код с обоими. Они повредили мой слабый мозг:-)

Затем я нашел Машинные Объекты (Мачо), очень простые и маленькие, и мне это нравится. Он поддерживает иерархические конечные автоматы, действия входа / выхода, историю, снимки конечного автомата, средства защиты, внутренние переходы, отсрочку событий, локальное хранилище состояний (с необязательным постоянством), поэтому для меня это удовлетворительная реализация диаграммы состояний Harel.

Этот код реализует часть FunctionMode диаграммы состояний с помощью Macho:

#include "Macho.hpp"

#include <exception>
#include <iostream>
using namespace std;

namespace FunctionMode {

struct FunctionMode;
struct F1Mode;
struct F2Mode;

// The Top state, containing all the others.
TOPSTATE(Top) {
    STATE(Top)
    // All the events of the state machine are just virtual functions.

    // Here we throw to mean that no inner state has implemented the event
    // handler and we consider that an error. This is optional, we could
    // just have an empty body or log the error.
    virtual void evF1() { throw std::exception(); }
    virtual void evF2() { throw std::exception(); }
    // evF3 and so on...
private:
    void init() { setState<FunctionMode>(); } // initial transition
};

SUBSTATE(FunctionMode, Top) {
    STATE(FunctionMode)
    virtual void evF1() { setState<F1Mode>(); }
    virtual void evF2() { setState<F2Mode>(); }
    // evF3, ...
private:
    void entry() { cout << "FunctionMode::entry" << endl; }
    void exit() { cout << "FunctionMode::exit" << endl; }
    void init() { setState<F1Mode>(); } // initial transition
};

SUBSTATE(F1Mode, FunctionMode) {
    STATE(F1Mode)
    virtual void evF1() {} // make the event an internal transition (by swallowing it)
private:
    void entry() { cout << "F1Mode::entry" << endl; }
    void exit() { cout << "F1Mode::exit" << endl; }
};

SUBSTATE(F2Mode, FunctionMode) {
    STATE(F2Mode)
    virtual void evF2() {} // make the event an internal transition (by swallowing it)
private:
    void entry() { cout << "F2Mode::entry" << endl; }
    void exit() { cout << "F2Mode::exit" << endl; }
};

} // namespace FunctionMode

int main() {

    Macho::Machine<FunctionMode::Top> sm;
    // Now the machine is already in F1Mode.

    // Macho has 2 methods for synchronous event dispatching:
    // First method:
    sm->evF1(); // <= this one will be swallowed by F1Mode::evF1()
    // Second method:
    sm.dispatch(Event(&FunctionMode::Top::evF2));

    return 0;
}

Запустив его, вы получите:

FunctionMode::entry
F1Mode::entry
F1Mode::exit
F2Mode::entry
F2Mode::exit
FunctionMode::exit

это показывает, что переходы являются внутренними.

На мой взгляд, чистый, легкий и компактный код:-)

[EDIT1] Первая версия кода не выполнила начальный переход FunctionMode -> F1Mode, Теперь это так.

Я знаю, что это старый вопрос, эти exit->enter в одном и том же состоянии раздражают.

Похоже, что для предотвращения повторного входа в себя вам необходимо: 1. Записать собственный обработчик в "собственном состоянии" 2. Записать меры защиты в родительский обработчик, которые запускают повторный вход в дочернее состояние.

Имхо, это недостаток в StateChart, для которого я еще не нашел хорошего решения - вызов свойства "пропустить переходы между состояниями повторного входа" для объекта statemachine был бы отличным.

Другие вопросы по тегам