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