PIP 5.6.1
Platform-Independent Primitives
Машина состояний

Машина состояний

Описание машины состояний

Машина состояний — это поведенческий паттерн проектирования, который позволяет объекту изменять свое поведение при изменении внутреннего состояния. Реализация в PIP основана на стандарте SCXML (State Chart XML).

Основные концепции SCXML

  • State (Состояние) — узел в дереве состояний, может быть атомарным или составным
  • Transition (Переход) — связь между состояниями, срабатывает при событии
  • Event (Событие) — триггер, вызывающий переход
  • Guard (Сторожевая функция) — условие, которое должно быть истинным для выполнения перехода
  • Action (Действие) — операция, выполняемая при переходе
  • Parallel (Параллельное состояние) — состояние, в котором все подсостояния активируются одновременно

Особенности реализации PIP

  • Нельзя создавать state, event, transition в стеке — все объекты создаются через new и управляются через указатели
  • Машина состояний — это корневой statePIStateMachine наследуется от PIStateBase
  • Все переходы и состояния удаляются автоматически — при уничтожении родительского объекта

Основные типы состояний

  • Атомарное состояние — состояние без вложенных подсостояний
  • Составное состояние — состояние, содержащее вложенные подсостояния
  • Финальное состояние — завершающее состояние, указывающее на окончание работы машины
  • Параллельное состояние — состояние, в котором все подсостояния активируются одновременно

Виды переходов

  • Обычный переход — срабатывает при получении определенного события
  • Переход по таймауту — срабатывает автоматически через заданный промежуток времени
  • Переход с guard — срабатывает только при выполнении определенного условия

PIP State Machine API

Основные классы

PIStateMachine

Основной класс машины состояний.

Ключевые методы:

  • bool start() — запускает выполнение машины состояний
  • bool isRunning() — проверяет, запущена ли машина
  • void setOnFinish(std::function<void()> f) — устанавливает коллбэк завершения
  • bool postEvent(int event_id, Args... args) — отправляет событие в машину состояний

PIStateBase

Базовый класс для всех состояний.

Ключевые методы:

  • 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) — устанавливает параллельный режим

PIStateLambda

Состояние с 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

PIStateFinal

Финальное состояние машины состояний.

PIStateFinal(std::function<void()> on_finish = nullptr, const PIString &n = {})
Финальное состояние, которое завершает родительское состояние при входе.
Definition: pistatemachine_state.h:227

PITransitionBase

Базовый класс для переходов.

Ключевые методы:

  • PITransitionBase *addGuard(std::function<R(Args...)> f) — добавляет сторожевую функцию
  • PITransitionBase *addAction(std::function<void()> a) — добавляет действие
  • void trigger() — запускает переход

PITransitionTimeout

Переход, срабатывающий через заданный промежуток времени.

Примеры использования

Простая машина состояний

#include "pistatemachine.h"
#include "pisystemtime.h"
// Создаем машину состояний
PIStateMachine *machine = new PIStateMachine("Main");
// Создаем состояния
[]() { piCout << "Entering Idle state\n"; },
[]() { piCout << "Exiting Idle state\n"; },
"Idle"
);
[]() { piCout << "Entering Running state\n"; },
[]() { piCout << "Exiting Running state\n"; },
"Running"
);
PIStateFinal *finish = new PIStateFinal(
[]() { piCout << "Machine finished\n"; },
"Finish"
);
// Добавляем состояния
machine->addState(idle);
machine->addState(running);
machine->addState(finish);
// Устанавливаем начальное состояние
idle->setInitialState(idle);
running->setInitialState(running);
machine->setInitialState(idle);
// Добавляем переходы
idle->addTransition(running, 1);
running->addTransition(finish, 2);
// Устанавливаем коллбэк завершения
machine->setOnFinish([]() { piCout << "Machine execution completed\n"; });
// Запускаем машину
machine->start();
// Отправляем события
machine->postEvent(1); // Перейти в Running
machine->postEvent(2); // Перейти в Finish
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-функциями

PIStateMachine *machine = new PIStateMachine("With Guards");
PIStateLambda *stateA = new PIStateLambda([]() { piCout << "State A\n"; }, nullptr, "A");
PIStateLambda *stateB = new PIStateLambda([]() { piCout << "State B\n"; }, nullptr, "B");
PIStateLambda *stateC = new PIStateLambda([]() { piCout << "State C\n"; }, nullptr, "C");
machine->addState(stateA);
machine->addState(stateB);
machine->addState(stateC);
stateA->setInitialState(stateA);
machine->setInitialState(stateA);
// Переход с guard-функцией
auto *t1 = stateA->addTransition(stateB, 1);
t1->addGuard([](int value) -> bool {
return value > 10;
});
// Переход с другой guard-функцией
auto *t2 = stateB->addTransition(stateC, 2);
t2->addGuard([](const PIString &msg) -> bool {
return msg == "allowed";
});
machine->start();
// Первый вызов не выполнится (value <= 10)
machine->postEvent(1, 5);
// Второй вызов выполнится (value > 10)
machine->postEvent(1, 15);
// Первый вызов не выполнится (msg != "allowed")
machine->postEvent(2, "test");
// Второй вызов выполнится (msg == "allowed")
machine->postEvent(2, "allowed");
PITransitionBase * addGuard(std::function< R(Args...)> f)
Задает guard-функцию, которая должна возвращать bool.
Definition: pistatemachine_transition.h:74

Машина состояний с таймаутами

PIStateMachine *machine = new PIStateMachine("With Timeout");
[]() { piCout << "Working...\n"; },
[]() { piCout << "Stop working\n"; },
"Working"
);
PIStateLambda *timeoutState = new PIStateLambda([]() { piCout << "Timeout occurred\n"; }, nullptr, "Timeout");
PIStateFinal *finish = new PIStateFinal([]() { piCout << "Done\n"; }, "Finish");
machine->addState(working);
machine->addState(timeoutState);
machine->addState(finish);
working->setInitialState(working);
machine->setInitialState(working);
// Добавляем переход по таймауту (через 5 секунд)
// Добавляем переход из timeoutState в finish
timeoutState->addTransition(finish, 1);
machine->start();
PITransitionTimeout * addTimeoutTransition(PIStateBase *target, PISystemTime timeout)
Добавляет переход по таймауту, который срабатывает пока это состояние активно.
Definition: pistatemachine_state.cpp:79
static PISystemTime fromSeconds(double v)
Создает время из "v" секунд
Definition: pisystemtime.h:374

Составные состояния

PIStateMachine *machine = new PIStateMachine("Compound States");
// Корневое состояние (машина)
PIStateLambda *root = new PIStateLambda([]() { piCout << "Root state\n"; }, nullptr, "Root");
// Составное состояние с подсостояниями
PIStateLambda *parent = new PIStateLambda([]() { piCout << "Parent state\n"; }, nullptr, "Parent");
PIStateLambda *child1 = new PIStateLambda([]() { piCout << "Child 1\n"; }, nullptr, "Child1");
PIStateLambda *child2 = new PIStateLambda([]() { piCout << "Child 2\n"; }, nullptr, "Child2");
parent->setInitialState(child1);
parent->addState(child1);
parent->addState(child2);
root->addState(parent);
machine->addState(root);
machine->setInitialState(root);
// Добавляем переходы
root->addTransition(parent, 1);
parent->addTransition(root, 2);
machine->start();
// Переход в parent
machine->postEvent(1);
// Переход обратно
machine->postEvent(2);

Параллельные состояния

PIStateMachine *machine = new PIStateMachine("Parallel States");
PIStateLambda *root = new PIStateLambda([]() { piCout << "Root\n"; }, nullptr, "Root");
PIStateLambda *parallel = new PIStateLambda([]() { piCout << "Parallel\n"; }, nullptr, "Parallel");
PIStateLambda *sub1 = new PIStateLambda([]() { piCout << "Substate 1\n"; }, nullptr, "Sub1");
PIStateLambda *sub2 = new PIStateLambda([]() { piCout << "Substate 2\n"; }, nullptr, "Sub2");
// Включаем параллельный режим
parallel->setParallel(true);
parallel->addState(sub1);
parallel->addState(sub2);
parallel->setInitialState(sub1);
root->addState(parallel);
machine->addState(root);
machine->setInitialState(root);
machine->start();
// При входе в parallel оба подсостояния активируются одновременно
void setParallel(bool yes)
Включает или выключает параллельную активацию дочерних состояний.
Definition: pistatemachine_state.h:119

Машина состояний с действиями

PIStateMachine *machine = new PIStateMachine("With Actions");
PIStateLambda *stateA = new PIStateLambda([]() { piCout << "State A\n"; }, nullptr, "A");
PIStateLambda *stateB = new PIStateLambda([]() { piCout << "State B\n"; }, nullptr, "B");
machine->addState(stateA);
machine->addState(stateB);
machine->setInitialState(stateA);
// Добавляем переход с действием
auto *t = stateA->addTransition(stateB, 1);
t->addAction([]() { piCout << "Action during transition\n"; });
machine->start();
machine->postEvent(1);
PITransitionBase * addAction(std::function< void()> a)
Задает действие, выполняемое при срабатывании перехода.
Definition: pistatemachine_transition.cpp:37

Потокобезопасность

Машина состояний PIP потокобезопасна. Метод postEvent использует очередь для обработки вложенных вызовов, что позволяет безопасно отправлять события из разных потоков.

Правила использования

Нельзя создавать в стеке

// ❌ НЕЛЬЗЯ
PIStateLambda state([]() {}, nullptr, "State");
// ✅ ПРАВИЛЬНО
PIStateLambda *state = new PIStateLambda([]() {}, nullptr, "State");

Правильная иерархия объектов

PIStateMachine *machine = new PIStateMachine("Main");
// Состояния создаются через new и добавляются в машину или другое состояние
PIStateLambda *state1 = new PIStateLambda([]() {}, nullptr, "State1");
PIStateLambda *state2 = new PIStateLambda([]() {}, nullptr, "State2");
machine->addState(state1);
machine->addState(state2);
// Переходы добавляются к состояниям через addTransition
state1->addTransition(state2, 1);
// Машина запускается
machine->start();

Очистка памяти

Все объекты (состояния, переходы) автоматически удаляются при уничтожении родительского объекта:

  • При уничтожении PIStateMachine удаляются все состояния и переходы
  • При уничтожении PIStateBase удаляются все дочерние состояния и переходы

Отладка

Для отладки можно использовать метод print() для вывода дерева состояний:

machine->print();
void print(PIString prefix={})
Печатает дерево состояний и активные ветви в piCout.
Definition: pistatemachine_state.cpp:87

Также можно использовать activeAtomics() для получения списка активных состояний.

Связанные модули

  • DateTime модуль для PISystemTime, используемого в переходах по таймауту