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`, используемого в переходах по таймауту
|