Машина состояний
Описание машины состояний
Машина состояний — это поведенческий паттерн проектирования, который позволяет объекту изменять свое поведение при изменении внутреннего состояния. Реализация в 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 = {})
Реализация состояния, которая перенаправляет виртуальные методы входа и выхода в callback-функции.
Definition: pistatemachine_state.h:195
Класс строки.
Definition: pistring.h:42
Финальное состояние машины состояний.
Финальное состояние, которое завершает родительское состояние при входе.
Definition: pistatemachine_state.h:227
Базовый класс для переходов.
Ключевые методы:
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)
Задает начальное дочернее состояние для непараллельного составного состояния.
Definition: pistatemachine_state.cpp:63
PITransitionBase * addTransition(PIStateBase *target, int event_id)
Добавляет переход по событию из этого состояния в target.
Definition: pistatemachine_state.cpp:71
void addState(PIStateBase *s)
Добавляет дочернее состояние, которым владеет это состояние.
Definition: pistatemachine_state.cpp:48
Корневой объект, который владеет и запускает иерархическую машину состояний.
Definition: pistatemachine.h:43
void setOnFinish(std::function< void()> f)
Задает callback-функцию, вызываемую при завершении машины.
Definition: pistatemachine.h:60
bool start()
Запускает машину из ее начальной активной конфигурации.
Definition: pistatemachine.cpp:28
bool postEvent(int event_id, Args... args)
Посылает событие в активные состояния и запускает первый подходящий переход.
Definition: pistatemachine.h:66
#define piCout
Definition: 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.
Definition: pistatemachine_transition.h:74
Машина состояний с таймаутами
[]() {
piCout <<
"Working...\n"; },
[]() {
piCout <<
"Stop working\n"; },
"Working"
);
timeoutState->addTransition(finish, 1);
PITransitionTimeout * addTimeoutTransition(PIStateBase *target, PISystemTime timeout)
Добавляет переход по таймауту, который срабатывает пока это состояние активно.
Definition: pistatemachine_state.cpp:79
static PISystemTime fromSeconds(double v)
Создает время из "v" секунд
Definition: 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)
Включает или выключает параллельную активацию дочерних состояний.
Definition: pistatemachine_state.h:119
Машина состояний с действиями
PITransitionBase * addAction(std::function< void()> a)
Задает действие, выполняемое при срабатывании перехода.
Definition: pistatemachine_transition.cpp:37
Потокобезопасность
Машина состояний PIP потокобезопасна. Метод postEvent использует очередь для обработки вложенных вызовов, что позволяет безопасно отправлять события из разных потоков.
Правила использования
Нельзя создавать в стеке
Правильная иерархия объектов
Очистка памяти
Все объекты (состояния, переходы) автоматически удаляются при уничтожении родительского объекта:
- При уничтожении
PIStateMachine удаляются все состояния и переходы
- При уничтожении
PIStateBase удаляются все дочерние состояния и переходы
Отладка
Для отладки можно использовать метод print() для вывода дерева состояний:
void print(PIString prefix={})
Печатает дерево состояний и активные ветви в piCout.
Definition: pistatemachine_state.cpp:87
Также можно использовать activeAtomics() для получения списка активных состояний.
Связанные модули
- DateTime модуль для
PISystemTime, используемого в переходах по таймауту