//! \~\file pistatemachine.h //! \brief //! \~english State machine. //! \~russian Машина состояний. //! \details //! \~english Main state machine class that manages states and transitions. //! \~russian Основной класс машины состояний, управляющий состояниями и переходами. //! \note //! \~english Thread-safe implementation with nested post handling. //! \~russian Потокобезопасная реализация с обработкой вложенных постов. //! \~\sa //! PIStateBase, PITransitionBase /* PIP - Platform Independent Primitives State machine Ivan Pelipenko peri4ko@yandex.ru This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ #ifndef pistatemachine_H #define pistatemachine_H #include "pistatemachine_state.h" #include "pistatemachine_transition.h" //! \~\ingroup StateMachine //! \~\brief //! \~english Root object that owns and runs a hierarchical state machine. //! \~russian Корневой объект, который владеет и запускает иерархическую машину состояний. class PIP_EXPORT PIStateMachine: public PIStateBase { public: //! \~english Creates a state machine with an optional name. //! \~russian Создает машину состояний с необязательным именем. PIStateMachine(const PIString & n = {}); //! \~english Starts the machine from its initial active configuration. //! \~russian Запускает машину из ее начальной активной конфигурации. bool start(); //! \~english Returns true while the machine is running. //! \~russian Возвращает true, пока машина запущена. bool isRunning() const { return is_running; } //! \~english Sets a callback invoked when the machine finishes. //! \~russian Задает callback-функцию, вызываемую при завершении машины. void setOnFinish(std::function f) { on_finish = f; } //! \~english Posts an event to active states and triggers the first matching transition. //! \~russian Посылает событие в активные состояния и запускает первый подходящий переход. template bool postEvent(int event_id, Args... args) { if (!is_running) return false; if (in_post.exchange(true)) { PIMutexLocker ml(nested_mutex); nested_posts.enqueue([this, event_id, args...] { postEvent(event_id, args...); }); // piCout << "queue nested post"; return false; } need_finish = false; PIScopeExitCall exit_call([this] { in_post = false; if (need_finish) { is_running = false; if (on_finish) on_finish(); } else { nested_mutex.lock(); while (nested_posts.isNotEmpty()) { auto np = nested_posts.dequeue(); nested_mutex.unlock(); np(); nested_mutex.lock(); } nested_mutex.unlock(); } }); PIVector active_states; gatherActiveStates(active_states); // piCout << "active" << active_states; for (auto * s: active_states) { for (auto * t: s->transitions) { if (t->eventID != event_id) continue; if (t->testGuard(args...)) { t->trigger(); return true; } } } return false; } private: void onFinish() override; bool is_running = false, need_finish = false; PIQueue> nested_posts; PIMutex nested_mutex; std::atomic_bool in_post = {false}; std::function on_finish; }; #endif