Машина состояний
Описание машины состояний
Машина состояний — это поведенческий паттерн проектирования, который позволяет объекту изменять свое поведение при изменении внутреннего состояния. Реализация в PIP основана на стандарте SCXML (State Chart XML).
Основные концепции SCXML
- State (Состояние) — узел в дереве состояний, может быть атомарным или составным
- Transition (Переход) — связь между состояниями, срабатывает при событии
- Event (Событие) — триггер, вызывающий переход
- Guard (Сторожевая функция) — условие, которое должно быть истинным для выполнения перехода
- Action (Действие) — операция, выполняемая при переходе
- Parallel (Параллельное состояние) — состояние, в котором все подсостояния активируются одновременно
Особенности реализации PIP
- Нельзя создавать state, event, transition в стеке — все объекты создаются через new и управляются через указатели
- Машина состояний — это корневой state — PIStateMachine наследуется от PIStateBase
- Все переходы и состояния удаляются автоматически — при уничтожении родительского объекта
Основные типы состояний
- Атомарное состояние — состояние без вложенных подсостояний
- Составное состояние — состояние, содержащее вложенные подсостояния
- Финальное состояние — завершающее состояние, указывающее на окончание работы машины
- Параллельное состояние — состояние, в котором все подсостояния активируются одновременно
Виды переходов
- Обычный переход — срабатывает при получении определенного события
- Переход по таймауту — срабатывает автоматически через заданный промежуток времени
- Переход с guard — срабатывает только при выполнении определенного условия
PIP State Machine API
Основные классы
Основной класс машины состояний.
Ключевые методы:
- bool start() — запускает выполнение машины состояний
- bool isRunning() — проверяет, запущена ли машина
- void setOnFinish(std::function<void()> f) — устанавливает коллбэк завершения
- bool postEvent(int event_id, Args... args) — отправляет событие в машину состояний
Базовый класс для всех состояний.
Ключевые методы:
- virtual void onEnter() — вызывается при входе в состояние
- virtual void onExit() — вызывается при выходе из состояния
- void addState(PIStateBase *s) — добавляет дочернее состояние
- PITransitionBase *addTransition(PIStateBase *target, int event_id) — добавляет переход
- PITransitionTimeout *addTimeoutTransition(PIStateBase *target, PISystemTime timeout) — добавляет переход по таймауту
- void setParallel(bool yes) — устанавливает параллельный режим
Состояние с lambda-коллбэками.
PIStateLambda(std::function<
void()> on_enter, std::function<
void()> on_exit =
nullptr,
const PIString &n = {})
PIStateLambda(std::function< void()> on_enter, std::function< void()> on_exit=nullptr, const PIString &n={})
Создает состояние с callback-функциями входа и выхода.
Определения pistatemachine_state.h:199
Класс строки.
Определения pistring.h:42
Финальное состояние машины состояний.
PIStateFinal(std::function< void()> on_finish=nullptr, const PIString &n={})
Создает финальное состояние с необязательной callback-функцией, выполняемой при входе.
Определения pistatemachine_state.h:231
Базовый класс для переходов.
Ключевые методы:
- PITransitionBase *addGuard(std::function<R(Args...)> f) — добавляет сторожевую функцию
- PITransitionBase *addAction(std::function<void()> a) — добавляет действие
- void trigger() — запускает переход
Переход, срабатывающий через заданный промежуток времени.
Примеры использования
Простая машина состояний
[]() {
piCout <<
"Entering Idle state\n"; },
[]() {
piCout <<
"Exiting Idle state\n"; },
"Idle"
);
[]() {
piCout <<
"Entering Running state\n"; },
[]() {
piCout <<
"Exiting Running state\n"; },
"Running"
);
[]() {
piCout <<
"Machine finished\n"; },
"Finish"
);
running->setInitialState(running);
running->addTransition(finish, 2);
void setInitialState(PIStateBase *s)
Задает начальное дочернее состояние для непараллельного составного состояния.
Определения pistatemachine_state.cpp:63
PITransitionBase * addTransition(PIStateBase *target, int event_id)
Добавляет переход по событию из этого состояния в target.
Определения pistatemachine_state.cpp:71
void addState(PIStateBase *s)
Добавляет дочернее состояние, которым владеет это состояние.
Определения pistatemachine_state.cpp:48
Финальное состояние, которое завершает родительское состояние при входе.
Определения pistatemachine_state.h:227
Реализация состояния, которая перенаправляет виртуальные методы входа и выхода в callback-функции.
Определения pistatemachine_state.h:195
Корневой объект, который владеет и запускает иерархическую машину состояний.
Определения pistatemachine.h:43
PIStateMachine(const PIString &n={})
Создает машину состояний с необязательным именем.
Определения pistatemachine.cpp:23
void setOnFinish(std::function< void()> f)
Задает callback-функцию, вызываемую при завершении машины.
Определения pistatemachine.h:60
bool start()
Запускает машину из ее начальной активной конфигурации.
Определения pistatemachine.cpp:28
bool postEvent(int event_id, Args... args)
Посылает событие в активные состояния и запускает первый подходящий переход.
Определения pistatemachine.h:66
#define piCout
Макрос для условного (piDebug) вывода в PICout(StdOut).
Определения picout.h:36
Типы и методы системного времени
Машина состояний с guard-функциями
return value > 10;
});
auto *t2 = stateB->addTransition(stateC, 2);
return msg == "allowed";
});
PITransitionBase * addGuard(std::function< R(Args...)> f)
Задает guard-функцию, которая должна возвращать bool.
Определения pistatemachine_transition.h:74
Машина состояний с таймаутами
[]() {
piCout <<
"Working...\n"; },
[]() {
piCout <<
"Stop working\n"; },
"Working"
);
timeoutState->addTransition(finish, 1);
PITransitionTimeout * addTimeoutTransition(PIStateBase *target, PISystemTime timeout)
Добавляет переход по таймауту, который срабатывает пока это состояние активно.
Определения pistatemachine_state.cpp:79
static PISystemTime fromSeconds(double v)
Создает время из "v" секунд
Определения pisystemtime.h:374
Составные состояния
parent->addState(child1);
parent->addState(child2);
parent->addTransition(root, 2);
Параллельные состояния
parallel->addState(sub1);
parallel->addState(sub2);
parallel->setInitialState(sub1);
void setParallel(bool yes)
Включает или выключает параллельную активацию дочерних состояний.
Определения pistatemachine_state.h:119
Машина состояний с действиями
PITransitionBase * addAction(std::function< void()> a)
Задает действие, выполняемое при срабатывании перехода.
Определения pistatemachine_transition.cpp:37
Потокобезопасность
Машина состояний PIP потокобезопасна. Метод postEvent использует очередь для обработки вложенных вызовов, что позволяет безопасно отправлять события из разных потоков.
Правила использования
Нельзя создавать в стеке
Правильная иерархия объектов
Очистка памяти
Все объекты (состояния, переходы) автоматически удаляются при уничтожении родительского объекта:
- При уничтожении PIStateMachine удаляются все состояния и переходы
- При уничтожении PIStateBase удаляются все дочерние состояния и переходы
Отладка
Для отладки можно использовать метод print() для вывода дерева состояний:
void print(PIString prefix={})
Печатает дерево состояний и активные ветви в piCout.
Определения pistatemachine_state.cpp:87
Также можно использовать activeAtomics() для получения списка активных состояний.
Связанные модули
- DateTime модуль для PISystemTime, используемого в переходах по таймауту