715 lines
23 KiB
Markdown
715 lines
23 KiB
Markdown
\~\english \page state_machine State machine
|
||
\~\russian \page state_machine Машина состояний
|
||
|
||
\~\english
|
||
|
||
# State Machine
|
||
|
||
## State Machine Description
|
||
|
||
State machine is a behavioral design pattern that allows an object to change its behavior when its internal state changes.
|
||
Implementation in PIP is based on the **SCXML** (State Chart XML) standard.
|
||
|
||
### SCXML Concepts
|
||
|
||
- **State** — a node in the state tree, can be atomic or compound
|
||
- **Transition** — a connection between states that triggers on an event
|
||
- **Event** — a trigger that causes a transition
|
||
- **Guard** — a condition that must be true for a transition to execute
|
||
- **Action** — an operation executed during a transition
|
||
- **Parallel** — a state where all substates are activated simultaneously
|
||
|
||
### PIP State Machine Features
|
||
|
||
- **Cannot create state, event, transition on stack** — all objects are created via `new` and managed via pointers
|
||
- **State machine is the root state** — `PIStateMachine` inherits from `PIStateBase`
|
||
- **All transitions and states are automatically deleted** — when the parent object is destroyed
|
||
|
||
### State Types
|
||
|
||
- **Atomic state** — a state without nested substates
|
||
- **Compound state** — a state containing nested substates
|
||
- **Final state** — a terminating state indicating the end of machine execution
|
||
- **Parallel state** — a state where all substates are activated simultaneously
|
||
|
||
### Transition Types
|
||
|
||
- **Normal transition** — triggers on receiving a specific event
|
||
- **Timeout transition** — triggers automatically after a specified time interval
|
||
- **Guarded transition** — triggers only when a specific condition is met
|
||
|
||
## State Machine API
|
||
|
||
### Main Classes
|
||
|
||
#### PIStateMachine
|
||
|
||
Main state machine class.
|
||
|
||
**Key methods:**
|
||
|
||
- `bool start()` — starts state machine execution
|
||
- `bool isRunning()` — checks if state machine is running
|
||
- `void setOnFinish(std::function<void()> f)` — sets finish callback
|
||
- `bool postEvent(int event_id, Args... args)` — posts event to state machine
|
||
|
||
#### PIStateBase
|
||
|
||
Base class for all states.
|
||
|
||
**Key methods:**
|
||
|
||
- `virtual void onEnter()` — called when state is entered
|
||
- `virtual void onExit()` — called when state is exited
|
||
- `void addState(PIStateBase *s)` — adds child state
|
||
- `PITransitionBase *addTransition(PIStateBase *target, int event_id)` — adds transition
|
||
- `PITransitionTimeout *addTimeoutTransition(PIStateBase *target, PISystemTime timeout)` — adds timeout transition
|
||
- `void setParallel(bool yes)` — sets parallel mode
|
||
|
||
#### PIStateLambda
|
||
|
||
State with lambda callbacks.
|
||
|
||
\code{.cpp}
|
||
PIStateLambda(std::function<void()> on_enter, std::function<void()> on_exit = nullptr, const PIString &n = {})
|
||
\endcode
|
||
|
||
#### PIStateFinal
|
||
|
||
Final state of state machine.
|
||
|
||
\code{.cpp}
|
||
PIStateFinal(std::function<void()> on_finish = nullptr, const PIString &n = {})
|
||
\endcode
|
||
|
||
#### PITransitionBase
|
||
|
||
Base class for transitions.
|
||
|
||
**Key methods:**
|
||
|
||
- `PITransitionBase *addGuard(std::function<R(Args...)> f)` — adds guard function
|
||
- `PITransitionBase *addAction(std::function<void()> a)` — adds action
|
||
- `void trigger()` — triggers transition
|
||
|
||
#### PITransitionTimeout
|
||
|
||
Transition that triggers after a specified timeout.
|
||
|
||
## Usage Examples
|
||
|
||
### Simple State Machine
|
||
|
||
\code{.cpp}
|
||
#include "pistatemachine.h"
|
||
#include "pisystemtime.h"
|
||
|
||
// Create state machine
|
||
PIStateMachine *machine = new PIStateMachine("Main");
|
||
|
||
// Create states
|
||
PIStateLambda *idle = new PIStateLambda(
|
||
[]() { piCout << "Entering Idle state\n"; },
|
||
[]() { piCout << "Exiting Idle state\n"; },
|
||
"Idle"
|
||
);
|
||
PIStateLambda *running = new PIStateLambda(
|
||
[]() { piCout << "Entering Running state\n"; },
|
||
[]() { piCout << "Exiting Running state\n"; },
|
||
"Running"
|
||
);
|
||
PIStateFinal *finish = new PIStateFinal(
|
||
[]() { piCout << "Machine finished\n"; },
|
||
"Finish"
|
||
);
|
||
|
||
// Add states
|
||
machine->addState(idle);
|
||
machine->addState(running);
|
||
machine->addState(finish);
|
||
|
||
// Set initial state
|
||
idle->setInitialState(idle);
|
||
running->setInitialState(running);
|
||
machine->setInitialState(idle);
|
||
|
||
// Add transitions
|
||
idle->addTransition(running, 1);
|
||
running->addTransition(finish, 2);
|
||
|
||
// Set finish callback
|
||
machine->setOnFinish([]() { piCout << "Machine execution completed\n"; });
|
||
|
||
// Start machine
|
||
machine->start();
|
||
|
||
// Post events
|
||
machine->postEvent(1); // Transition to Running
|
||
machine->postEvent(2); // Transition to Finish
|
||
\endcode
|
||
|
||
### Guarded Transitions
|
||
|
||
\code{.cpp}
|
||
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);
|
||
|
||
// Transition with guard function
|
||
auto *t1 = stateA->addTransition(stateB, 1);
|
||
t1->addGuard([](int value) -> bool {
|
||
return value > 10;
|
||
});
|
||
|
||
// Transition with another guard function
|
||
auto *t2 = stateB->addTransition(stateC, 2);
|
||
t2->addGuard([](const PIString &msg) -> bool {
|
||
return msg == "allowed";
|
||
});
|
||
|
||
machine->start();
|
||
|
||
// First call will not execute (value <= 10)
|
||
machine->postEvent(1, 5);
|
||
|
||
// Second call will execute (value > 10)
|
||
machine->postEvent(1, 15);
|
||
|
||
// First call will not execute (msg != "allowed")
|
||
machine->postEvent(2, "test");
|
||
|
||
// Second call will execute (msg == "allowed")
|
||
machine->postEvent(2, "allowed");
|
||
\endcode
|
||
|
||
### Timeout Transitions
|
||
|
||
\code{.cpp}
|
||
PIStateMachine *machine = new PIStateMachine("With Timeout");
|
||
|
||
PIStateLambda *working = new PIStateLambda(
|
||
[]() { 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);
|
||
|
||
// Add timeout transition (after 5 seconds)
|
||
working->addTimeoutTransition(timeoutState, PISystemTime::fromSeconds(5));
|
||
|
||
// Add transition from timeoutState to finish
|
||
timeoutState->addTransition(finish, 1);
|
||
|
||
machine->start();
|
||
\endcode
|
||
|
||
### Compound States
|
||
|
||
\code{.cpp}
|
||
PIStateMachine *machine = new PIStateMachine("Compound States");
|
||
|
||
// Root state (machine)
|
||
PIStateLambda *root = new PIStateLambda([]() { piCout << "Root state\n"; }, nullptr, "Root");
|
||
|
||
// Compound state with substates
|
||
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);
|
||
|
||
// Add transitions
|
||
root->addTransition(parent, 1);
|
||
parent->addTransition(root, 2);
|
||
|
||
machine->start();
|
||
|
||
// Transition to parent
|
||
machine->postEvent(1);
|
||
|
||
// Transition back
|
||
machine->postEvent(2);
|
||
\endcode
|
||
|
||
### Parallel States
|
||
|
||
\code{.cpp}
|
||
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");
|
||
|
||
// Enable parallel mode
|
||
parallel->setParallel(true);
|
||
parallel->addState(sub1);
|
||
parallel->addState(sub2);
|
||
parallel->setInitialState(sub1);
|
||
root->addState(parallel);
|
||
|
||
machine->addState(root);
|
||
machine->setInitialState(root);
|
||
|
||
machine->start();
|
||
|
||
// When entering parallel, both substates are activated simultaneously
|
||
\endcode
|
||
|
||
### Transitions with Actions
|
||
|
||
\code{.cpp}
|
||
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);
|
||
|
||
// Add transition with action
|
||
auto *t = stateA->addTransition(stateB, 1);
|
||
t->addAction([]() { piCout << "Action during transition\n"; });
|
||
|
||
machine->start();
|
||
machine->postEvent(1);
|
||
\endcode
|
||
|
||
## Thread Safety
|
||
|
||
State machine is thread-safe. The `postEvent` method uses a queue to handle nested calls, allowing safe event posting from
|
||
different threads.
|
||
|
||
## Usage Rules
|
||
|
||
### Cannot Create on Stack
|
||
|
||
\code{.cpp}
|
||
// ❌ WRONG
|
||
PIStateLambda state([]() {}, nullptr, "State");
|
||
|
||
// ✅ CORRECT
|
||
PIStateLambda *state = new PIStateLambda([]() {}, nullptr, "State");
|
||
\endcode
|
||
|
||
### Proper Object Hierarchy
|
||
|
||
\code{.cpp}
|
||
PIStateMachine *machine = new PIStateMachine("Main");
|
||
|
||
// States are created via new and added to machine or another state
|
||
PIStateLambda *state1 = new PIStateLambda([]() {}, nullptr, "State1");
|
||
PIStateLambda *state2 = new PIStateLambda([]() {}, nullptr, "State2");
|
||
|
||
machine->addState(state1);
|
||
machine->addState(state2);
|
||
|
||
// Transitions are added to states via addTransition
|
||
state1->addTransition(state2, 1);
|
||
|
||
// Machine is started
|
||
machine->start();
|
||
\endcode
|
||
|
||
### Memory Cleanup
|
||
|
||
All objects (states, transitions) are automatically deleted when the parent object is destroyed:
|
||
|
||
- When `PIStateMachine` is destroyed, all states and transitions are deleted
|
||
- When `PIStateBase` is destroyed, all child states and transitions are deleted
|
||
|
||
## Debugging
|
||
|
||
For debugging, you can use the `print()` method to output the state tree:
|
||
|
||
\code{.cpp}
|
||
machine->print();
|
||
\endcode
|
||
|
||
Also, you can use `activeAtomics()` to get a list of active states.
|
||
|
||
## Related Modules
|
||
|
||
- \ref DateTime module for `PISystemTime` used in timeout transitions
|
||
|
||
\~\russian
|
||
|
||
# Машина состояний
|
||
|
||
## Описание машины состояний
|
||
|
||
Машина состояний — это поведенческий паттерн проектирования, который позволяет объекту изменять свое поведение при изменении
|
||
внутреннего состояния. Реализация в 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
|
||
|
||
### Основные классы
|
||
|
||
#### 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-коллбэками.
|
||
|
||
\code{.cpp}
|
||
PIStateLambda(std::function<void()> on_enter, std::function<void()> on_exit = nullptr, const PIString &n = {})
|
||
\endcode
|
||
|
||
#### PIStateFinal
|
||
|
||
Финальное состояние машины состояний.
|
||
|
||
\code{.cpp}
|
||
PIStateFinal(std::function<void()> on_finish = nullptr, const PIString &n = {})
|
||
\endcode
|
||
|
||
#### PITransitionBase
|
||
|
||
Базовый класс для переходов.
|
||
|
||
**Ключевые методы:**
|
||
|
||
- `PITransitionBase *addGuard(std::function<R(Args...)> f)` — добавляет сторожевую функцию
|
||
- `PITransitionBase *addAction(std::function<void()> a)` — добавляет действие
|
||
- `void trigger()` — запускает переход
|
||
|
||
#### PITransitionTimeout
|
||
|
||
Переход, срабатывающий через заданный промежуток времени.
|
||
|
||
## Примеры использования
|
||
|
||
### Простая машина состояний
|
||
|
||
\code{.cpp}
|
||
#include "pistatemachine.h"
|
||
#include "pisystemtime.h"
|
||
|
||
// Создаем машину состояний
|
||
PIStateMachine *machine = new PIStateMachine("Main");
|
||
|
||
// Создаем состояния
|
||
PIStateLambda *idle = new PIStateLambda(
|
||
[]() { piCout << "Entering Idle state\n"; },
|
||
[]() { piCout << "Exiting Idle state\n"; },
|
||
"Idle"
|
||
);
|
||
PIStateLambda *running = new PIStateLambda(
|
||
[]() { 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
|
||
\endcode
|
||
|
||
### Машина состояний с guard-функциями
|
||
|
||
\code{.cpp}
|
||
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");
|
||
\endcode
|
||
|
||
### Машина состояний с таймаутами
|
||
|
||
\code{.cpp}
|
||
PIStateMachine *machine = new PIStateMachine("With Timeout");
|
||
|
||
PIStateLambda *working = new PIStateLambda(
|
||
[]() { 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 секунд)
|
||
working->addTimeoutTransition(timeoutState, PISystemTime::fromSeconds(5));
|
||
|
||
// Добавляем переход из timeoutState в finish
|
||
timeoutState->addTransition(finish, 1);
|
||
|
||
machine->start();
|
||
\endcode
|
||
|
||
### Составные состояния
|
||
|
||
\code{.cpp}
|
||
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);
|
||
\endcode
|
||
|
||
### Параллельные состояния
|
||
|
||
\code{.cpp}
|
||
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 оба подсостояния активируются одновременно
|
||
\endcode
|
||
|
||
### Машина состояний с действиями
|
||
|
||
\code{.cpp}
|
||
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);
|
||
\endcode
|
||
|
||
## Потокобезопасность
|
||
|
||
Машина состояний PIP потокобезопасна. Метод `postEvent` использует очередь для обработки вложенных вызовов, что позволяет
|
||
безопасно отправлять события из разных потоков.
|
||
|
||
## Правила использования
|
||
|
||
### Нельзя создавать в стеке
|
||
|
||
\code{.cpp}
|
||
// ❌ НЕЛЬЗЯ
|
||
PIStateLambda state([]() {}, nullptr, "State");
|
||
|
||
// ✅ ПРАВИЛЬНО
|
||
PIStateLambda *state = new PIStateLambda([]() {}, nullptr, "State");
|
||
\endcode
|
||
|
||
### Правильная иерархия объектов
|
||
|
||
\code{.cpp}
|
||
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();
|
||
\endcode
|
||
|
||
### Очистка памяти
|
||
|
||
Все объекты (состояния, переходы) автоматически удаляются при уничтожении родительского объекта:
|
||
|
||
- При уничтожении `PIStateMachine` удаляются все состояния и переходы
|
||
- При уничтожении `PIStateBase` удаляются все дочерние состояния и переходы
|
||
|
||
## Отладка
|
||
|
||
Для отладки можно использовать метод `print()` для вывода дерева состояний:
|
||
|
||
\code{.cpp}
|
||
machine->print();
|
||
\endcode
|
||
|
||
Также можно использовать `activeAtomics()` для получения списка активных состояний.
|
||
|
||
## Связанные модули
|
||
|
||
- \ref DateTime модуль для `PISystemTime`, используемого в переходах по таймауту
|