32 #include <IceUtil/Time.h>
43 using namespace StateUtilFunctions;
46 #define MAX_USER_CODE_DURATION 100 //milliseconds
63 cimpl->transitionFunctions =
source.cimpl->transitionFunctions;
64 cimpl->localProfilers =
source.cimpl->localProfilers;
65 cimpl->profilersDisabled =
source.cimpl->profilersDisabled;
71 if (
cimpl->__runningTask)
73 cimpl->__runningTask->stop();
77 std::unique_lock lock(
cimpl->__finishedMutex);
78 cimpl->__finished =
true;
81 cimpl->__finishedCondition.notify_all();
86 std::unique_lock lock(
cimpl->__finishedMutex);
87 return cimpl->__finished;
95 std::unique_lock lock(
cimpl->__finishedMutex);
99 throw LocalException(
"You can only wait for toplevel states to finish!");
102 while (!
cimpl->__finished)
106 cimpl->__finishedCondition.wait(lock);
110 if (
cimpl->__finishedCondition.wait_for(lock, std::chrono::milliseconds(timeoutMs)) == std::cv_status::timeout)
112 throw LocalException(
"Statechart did not finish in time");
136 for (
auto& state : subStateList)
142 if (remote->getState() < eManagedIceObjectStarted)
144 ARMARX_INFO <<
"Waiting for remote state " << remote->getGlobalHierarchyString();
145 objectScheduler->waitForDependencies();
153 if (tempInputParameters.size() > 0)
156 ->mapFromOutput(
"*",
"*")
157 ->_addSourceDictionary(eOutput, tempInputParameters)
158 ->setTargetDictToGreedy(greedyInputDictionary)
159 ->_applyMapping(inputParameters);
170 activeSubstate =
nullptr;
171 impl->triggeredEndstateEvent = ev;
172 ARMARX_DEBUG <<
"Substate finished with event " << ev->eventName;
219 StringVariantContainerBaseMap srcSetOutputParams;
226 auto applyMapping = [&](
const ParameterMappingIceBasePtr & icepm,
StateParameterMap & targetMap,
bool greedy)
229 PMPtr pm = PMPtr::dynamicCast(icepm);
233 pm->setTargetDictToGreedy(greedy);
236 pm->_addSourceDictionary(eParent, parentSetInputParams)
237 ->_addSourceDictionary(eOutput, srcSetOutputParams)
238 ->_addSourceDictionary(eEvent, event->properties)
239 ->_applyMapping(targetMap);
242 applyMapping(t.mappingToParentStatesLocal, localParameters,
false);
243 applyMapping(t.mappingToParentStatesOutput, outputParameters,
false);
244 applyMapping(t.mappingToNextStatesInput, StateBasePtr::dynamicCast(t.destinationState)->inputParameters, StateBasePtr::dynamicCast(t.destinationState)->greedyInputDictionary);
246 std::string paramCheckOutput;
249 auto it =
cimpl->transitionFunctions.find(transitionId);
250 if (it !=
cimpl->transitionFunctions.end())
254 f(
this, StateIceBasePtr::dynamicCast(t.destinationState), StateIceBasePtr::dynamicCast(t.sourceState));
261 error.
infos.push_back(StateBasePtr::dynamicCast(t.destinationState)->stateName);
262 error.
infos.push_back(paramCheckOutput);
279 <<
"\t- The input parameters of the destination state were not fully set.\n\tDestination state: '" << transitionError.
infos.at(0) <<
"'\n" <<
280 transitionError.
infos.at(1);
287 <<
"\t- The state '" << stateName <<
"' does not expect the event '" <<
event->eventName <<
"' while in substate '" << StateBasePtr::dynamicCast(activeSubstate)->stateName <<
"'.\n" <<
flush;
302 STATEINFO <<
"TRANSITION due to Event '" <<
event->eventName <<
"' (EventReceiver: '" <<
event->eventReceiverName <<
"')..." <<
flush;
303 boost::recursive_mutex::scoped_lock lock(
impl->__processEventMutex);
315 ARMARX_WARNING <<
"a parent state has events to process. this event will be skipped" <<
flush;
330 if (src && src->__hasSubstates() && src->__hasActiveSubstate())
332 ARMARX_INFO <<
"<<<< Trying to break substates of " << src->stateName <<
flush;
335 if (src->__breakActiveSubstate(event))
342 cimpl->__unbreakableBuffer.push(event);
345 ARMARX_INFO <<
"got event while in unbreakable substate: parent stateName:" << stateName <<
flush;
358 t.sourceState = activeSubstate;
362 activeSubstate =
nullptr;
363 bool destinationStateInitialized = StateBasePtr::dynamicCast(t.destinationState)->waitForInitialization();
364 ARMARX_CHECK_EXPRESSION(destinationStateInitialized) << StateBasePtr::dynamicCast(t.destinationState)->stateName;
369 activeSubstate = t.destinationState;
370 if (t.sourceState && activeSubstate && !
cimpl->profilersDisabled)
374 localProfiler->logStatechartTransition(
getGlobalHierarchyString(), StateIceBasePtr::dynamicCast(t.sourceState), StateIceBasePtr::dynamicCast(t.destinationState), t.evt->eventName);
375 localProfiler->logStatechartTransitionWithParameters(t);
380 StateControllerPtr::dynamicCast(t.destinationState)->_baseOnEnter();
383 if (src && src->unbreakable && src->eventsDelayed)
389 if (StateIceBasePtr::dynamicCast(t.destinationState)->unbreakable)
391 StateIceBasePtr::dynamicCast(t.destinationState)->eventsDelayed =
true;
416 ARMARX_WARNING <<
"The state '" << stateName <<
"' does not have an activeSubstate and therefore cannot process any events." <<
flush;
429 int selectedTransitionIndex = -1;
431 for (
unsigned int i = 0; i < transitions.size(); i++)
434 const TransitionIceBase& transition = transitions.at(i);
437 (transition.sourceState == sourceState || transition.fromAll)
438 && transition.evt->eventName == event->eventName
439 && (sourceState->stateName == event->eventReceiverName || event->eventReceiverName ==
EVENTTOALL)
442 selectedTransitionIndex = i;
448 if (selectedTransitionIndex == -1)
454 const TransitionIceBase& transition = transitions.at(selectedTransitionIndex);
456 resultTransition = transition;
467 (transition.sourceState != sourceState && !transition.fromAll)
468 || transition.evt->eventName != event->eventName
469 || (sourceState && sourceState->stateName != event->eventReceiverName && event->eventReceiverName !=
EVENTTOALL)
477 if (transition.sourceState && transition.fromAll)
485 PMPtr transitionMapping = PMPtr::dynamicCast(transition.mappingToNextStatesInput);
488 if (transitionMapping && sourceState)
493 transitionMapping->setTargetDictToGreedy(StateBasePtr::dynamicCast(transition.destinationState)->greedyInputDictionary);
496 transitionMapping->_addSourceDictionary(eParent, parentSetInputParams)
497 ->_addSourceDictionary(eOutput, srcSetOutputParams)
498 ->_addSourceDictionary(eEvent, event->properties)
499 ->_applyMapping(inputCopy);
500 std::string paramCheckOutput;
505 error.
infos.push_back(destinationState->stateName +
": " + paramCheckOutput);
516 return cimpl->__unbreakableBuffer.size();
522 cimpl->__eventBufferedDueToUnbreakableState = eventBuffered;
524 for (
unsigned int i = 0; i < subStateList.size(); ++i)
529 if (state->__getUnbreakableBufferSize() > 0)
531 eventBuffered =
true;
534 StateControllerPtr::dynamicCast(subStateList.at(i))->__notifyEventBufferedDueToUnbreakableState(eventBuffered);
545 if (!ptr &&
impl->__parentState)
549 armarx::StateIceBase* base = newParentAsController.get();
550 *base = *
impl->__parentState;
551 ARMARX_WARNING <<
"Had to call factory manually for StateIceBase in "
552 <<
impl->__parentState->globalStateIdentifier;
553 return newParentAsController;
563 boost::recursive_mutex::scoped_lock lock(
impl->__processEventMutex);
567 result = StateControllerPtr::dynamicCast(activeSubstate)->_baseOnBreak(event);
571 activeSubstate =
nullptr;
587 ARMARX_VERBOSE <<
"processing " <<
cimpl->__unbreakableBuffer.front()->eventName <<
" in unbreakableBuffer - eventprocessorstate:" << stateName <<
" - activeState: " << StateBasePtr::dynamicCast(activeSubstate)->stateName <<
flush;
588 impl->__eventUnbreakableBufferMutex.lock();
590 cimpl->__unbreakableBuffer.pop();
591 impl->__eventUnbreakableBufferMutex.unlock();
603 StateControllerPtr::dynamicCast(
__getParentState())->__processBufferedEvents();
611 for (
unsigned int i = 0; i < subStateList.size(); ++i)
613 RemoteStatePtr remoteStatePtr = RemoteStatePtr::dynamicCast(subStateList.at(i));
618 ARMARX_INFO <<
"Waiting for RemoteState " << remoteStatePtr->getName();
620 while (!(remoteStatePtr->getState() >= eManagedIceObjectStarted))
625 ARMARX_INFO <<
"RemoteState " << remoteStatePtr->getName() <<
"started.";
633 for (
unsigned int i = 0; i < transitions.size(); ++i)
636 if (transitions[i].evt->eventName == transition.evt->eventName
637 && (transition.fromAll || transitions[i].sourceState == transition.sourceState)
638 && transitions[i].destinationState == transition.destinationState
660 if (
cimpl->__runningTask)
665 if (
cimpl->__runningTask->isRunning())
667 ARMARX_VERBOSE <<
"Waiting for running task of " << stateName <<
" to finish";
670 cimpl->__runningTask->stop();
672 catch (IceUtil::ThreadSyscallException& e)
678 eventsDelayed =
false;
681 ARMARX_DEBUG <<
"Resetting local and output parameters of state " << stateName;
686 std::string paramCheckOutput;
690 throw LocalException(
"Not all required inputparameters of the state '" + stateName +
"' are set:\n" + paramCheckOutput);
694 impl->cancelEnteringSubstates =
false;
696 impl->triggeredEndstateEvent =
nullptr;
698 impl->visitCounter++;
707 if (!
cimpl->profilersDisabled)
722 ARMARX_ERROR <<
"onEnter() of State " << globalStateIdentifier <<
" of class " << stateClassName <<
" failed. Ignoring substates and sending Failure.\n" <<
GetHandledExceptionString();
732 if (!
cimpl->profilersDisabled)
740 IceUtil::Time duration = IceUtil::Time::now() - executionStart;
744 ARMARX_WARNING <<
"onEnter() of state '" + stateName +
"' took more than " <<
MAX_USER_CODE_DURATION <<
" ms (In fact: " << duration.toMilliSeconds() <<
" ms). The onEnter() method should not calculate complex operations." <<
flush;
752 if (!
impl->cancelEnteringSubstates)
756 impl->cancelEnteringSubstates =
false;
767 if (initialStateMapping)
769 PMPtr mapping = ParameterMappingPtr::dynamicCast(initialStateMapping);
770 mapping->_addSourceDictionary(eParent, combinedMap)
771 ->setTargetDictToGreedy(StateBasePtr::dynamicCast(initState)->greedyInputDictionary)
772 ->_applyMapping(StateBasePtr::dynamicCast(initState)->inputParameters);
775 activeSubstate = initState;
777 if (!
cimpl->profilersDisabled && activeSubstate)
781 localProfiler->logStatechartTransition(
getGlobalHierarchyString(),
nullptr, StateIceBasePtr::dynamicCast(activeSubstate),
"InitialTransition");
784 t.sourceState =
nullptr;
785 t.destinationState = activeSubstate;
786 EventBasePtr e(
new EventBase());
787 e->eventName =
"InitialTransition";
789 localProfiler->logStatechartTransitionWithParameters(t);
792 StateControllerPtr::dynamicCast(initState)->_baseOnEnter();
799 STATEINFO <<
"Entering substates of " << stateName <<
" has been canceled";
813 if (!
impl->__useRunFunction)
819 if (!
impl->__parentState ||
impl->__parentState->activeSubstate.get() ==
this)
822 cimpl->__runningTask->start();
842 ARMARX_ERROR_S <<
"run() of State " << globalStateIdentifier <<
" of class " << stateClassName <<
" failed for unhandled reason. Ignoring substates and sending Failure\n" <<
GetHandledExceptionString();
858 impl->cancelEnteringSubstates =
true;
860 if (
cimpl->__runningTask)
862 cimpl->__runningTask->stop(
false);
873 throw exceptions::local::eStatechartLogicError(
"baseOnExit was called before substates were finished - baseOnBreak must be called instead. ActiveSubstate: " + StateBasePtr::dynamicCast(activeSubstate)->stateName);
886 if (!
cimpl->profilersDisabled)
901 IceUtil::Time duration = IceUtil::Time::now() - executionStart;
911 ARMARX_WARNING <<
"onExit() of state '" + stateName +
"' took more than " <<
MAX_USER_CODE_DURATION <<
" ms (In fact: " << duration.toMilliSeconds() <<
" ms). The onEnter() method should not calculate complex operations." <<
flush;
914 std::string paramCheckOutput;
918 throw LocalException(
"Not all required Outputparameters of state '" + stateName +
"' are set:\n" + paramCheckOutput);
926 std::unique_lock lock(
cimpl->__finishedMutex);
927 cimpl->__finished =
true;
928 cimpl->__finishedCondition.notify_all();
939 if (
cimpl->__runningTask)
941 cimpl->__runningTask->stop(
false);
956 eventsDelayed =
true;
962 if (StateBasePtr::dynamicCast(activeSubstate)->unbreakable)
965 StateBasePtr::dynamicCast(activeSubstate)->eventsDelayed =
true;
979 if (
cimpl->__runningTask)
981 cimpl->__runningTask->stop(
false);
991 if (!
cimpl->profilersDisabled)
1002 ARMARX_ERROR <<
"onBreak() of State " << globalStateIdentifier <<
" of class " << stateClassName <<
" failed. Ignoring substates and sending Failure.\n" <<
GetHandledExceptionString();
1008 IceUtil::Time duration = IceUtil::Time::now() - executionStart;
1012 ARMARX_WARNING <<
"onEnter() of state '" + stateName +
"' took more than " <<
MAX_USER_CODE_DURATION <<
" ms (In fact: " << duration.toMilliSeconds() <<
" ms). The onEnter() method should not calculate complex operations." <<
flush;
1024 std::unique_lock lock(
cimpl->__finishedMutex);
1025 cimpl->__finished =
true;
1026 cimpl->__finishedCondition.notify_all();
1036 cimpl->localProfilers.insert(profiler);
1037 if (recursiveLevels < 0)
1041 else if (recursiveLevels == 0)
1045 else if (recursiveLevels > 0)
1050 for (AbstractStateIceBasePtr state : subStateList)
1054 if (stateController)
1056 stateController->addProfilerRecursive(profiler, recursiveLevels);
1064 cimpl->localProfilers.erase(profiler);
1065 if (recursiveLevels < 0)
1069 else if (recursiveLevels == 0)
1073 else if (recursiveLevels > 0)
1078 for (AbstractStateIceBasePtr state : subStateList)
1082 if (stateController)
1084 stateController->addProfilerRecursive(profiler, recursiveLevels);
1094 if (!
cimpl->__runningTask)
1099 return cimpl->__runningTask->isStopped();
1104 if (!
cimpl->__runningTask)
1108 return cimpl->__runningTask->isFinished();
1114 if (
cimpl->__runningTask)
1116 cimpl->__runningTask->join();
1122 cimpl->profilersDisabled = disable;
1127 ARMARX_INFO <<
"Regstering transition code for " << t.evt->eventName;
1134 return getTransitionID(t.evt->eventName, (t.sourceState ? StateBasePtr::dynamicCast(t.sourceState)->stateName : std::string(
"")));
1139 return sourceStateName +
":" + eventName;
1145 for (
const TransitionIceBase& t : transitions)
1147 if (t.evt->eventName == eventName && StateBasePtr::dynamicCast(t.sourceState)->stateName == sourceStateName)
1153 ARMARX_INFO <<
"Could not find transition in " << transitions.size() <<
" transitions";
1172 return !
cimpl->__eventBufferedDueToUnbreakableState;
1179 for (
unsigned int i = 0; i < subStateList.size(); i++)
1181 StateControllerPtr::dynamicCast(subStateList[i])->disableRunFunction();
1185 impl->__useRunFunction =
false;
1187 if (
cimpl->__runningTask &&
cimpl->__runningTask->isRunning())
1189 ARMARX_VERBOSE <<
"State with name '" << stateName <<
"' is waiting for the RunFunction";
1190 cimpl->__runningTask->stop();
1193 cimpl->__runningTask =
nullptr;