/* PIP - Platform Independent Primitives State machine Ivan Pelipenko peri4ko@yandex.ru, Andrey Bychkov work.a.b@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 . */ #include "pistatemachine_state.h" #include "pistatemachine_transition.h" #include "pitranslator.h" PIStateBase::~PIStateBase() { piDeleteAll(transitions); piDeleteAll(children); } PIVector PIStateBase::activeChildren() const { if (!isParallel()) { if (active_state) return {active_state}; return {}; } if (isActive()) return children; return {}; } PIVector PIStateBase::activeAtomics() const { PIVector ret; gatherActiveAtomicStates(ret); return ret; } void PIStateBase::addState(PIStateBase * s) { children << s; s->parent_state = this; propagateRoot(root); } void PIStateBase::addStates(PIVector s) { children << s; for (auto * i: s) i->parent_state = this; propagateRoot(root); } void PIStateBase::setInitialState(PIStateBase * s) { if (children.contains(s)) initial_state = s; else initial_state = nullptr; } PITransitionBase * PIStateBase::addTransition(PIStateBase * target, int event_id) { PITransitionBase * ret = new PITransitionBase(this, target, event_id); ret->root = root; transitions << ret; return ret; } PITransitionTimeout * PIStateBase::addTimeoutTransition(PIStateBase * target, PISystemTime timeout) { PITransitionTimeout * ret = new PITransitionTimeout(this, target, timeout); ret->root = root; transitions << ret; return ret; } void PIStateBase::print(PIString prefix) { auto ac = activeChildren(); PICout(PICoutManipulators::AddNone) << prefix << "[" << (isActive() ? "*" : " ") << "] \"" << getName() << "\""; if (ac.isNotEmpty()) piCout << ", active" << activeChildren(); else piCout << ""; prefix += " "; for (auto * c: children) c->print(prefix); } bool PIStateBase::start(bool force) { if (!force && isActive()) return true; if (isAtomic()) { setActive(true); return true; } else { if (!isParallel()) { if (initial_state) { setActive(true); return initial_state->start(); } else { piCout << "Error: \"%1\" no initial state!"_tr("PIStateMachine").arg(getName()); return false; } } else { setActive(true); for (auto * s: children) { if (!s->start()) return false; } return true; } } return false; } void PIStateBase::setActive(bool yes) { bool prev_active = is_active; is_active = yes; if (parent_state && yes) parent_state->childActived(this); if (!prev_active && is_active) { onEnter(); for (auto * t: transitions) t->enabled(); // if (isParallel()) setChildrenActive(true); } if (prev_active && !is_active) { active_state = nullptr; if (isParallel()) setChildrenActiveRecursive(false); onExit(); for (auto * t: transitions) t->disabled(); } } void PIStateBase::setActiveRecursive(bool yes) { if (yes) setActive(true); for (auto * c: children) c->setActiveRecursive(yes); if (!yes) setActive(false); } void PIStateBase::setChildrenActive(bool yes) { for (auto * c: children) c->setActive(yes); } void PIStateBase::setChildrenActiveRecursive(bool yes) { for (auto * c: children) c->setActiveRecursive(yes); } void PIStateBase::activeChild(PIStateBase * c) { setActive(true); if (isParallel()) for (auto * s: children) { if (c != s) s->start(); } else { if (!c) c = initial_state; if (c) c->setActive(true); } } void PIStateBase::childActived(PIStateBase * s) { if (!isParallel()) { active_state = s; if (active_state->isFinal()) onFinish(); } else active_state = nullptr; } void PIStateBase::propagateRoot(PIStateMachine * r) { if (is_root) root = reinterpret_cast(this); else root = r; for (auto * t: transitions) t->root = root; for (auto * c: children) c->propagateRoot(root); } PIVector PIStateBase::gatherStates() { PIVector ret, slice, prev_slice({this}); while (prev_slice.isNotEmpty()) { slice.clear(); for (auto s: prev_slice) { ret << s; slice << s->children; } prev_slice.swap(slice); } return ret; } void PIStateBase::gatherActiveStates(PIVector & output) { for (auto * c: children) c->gatherActiveStates(output); if (isActive()) output << this; } void PIStateBase::gatherActiveAtomicStates(PIVector & output) const { for (auto * c: children) c->gatherActiveAtomicStates(output); if (isActive() && isAtomic()) output << const_cast(this); } void PIStateBase::gatherPathToMachine(PIVector & output) { if (isStateMachine()) return; output << this; if (parent()) parent()->gatherPathToMachine(output); } PIVector PIStateBase::pathToMachine() { PIVector ret; gatherPathToMachine(ret); return ret; }