27#include <IceUtil/Time.h>
46#define MAX_USER_CODE_DURATION 100
60 cimpl->transitionFunctions = source.cimpl->transitionFunctions;
61 cimpl->localProfilers = source.cimpl->localProfilers;
62 cimpl->profilersDisabled = source.cimpl->profilersDisabled;
68 if (
cimpl->__runningTask)
70 cimpl->__runningTask->stop();
74 std::unique_lock lock(
cimpl->__finishedMutex);
75 cimpl->__finished =
true;
78 cimpl->__finishedCondition.notify_all();
84 std::unique_lock lock(
cimpl->__finishedMutex);
85 return cimpl->__finished;
93 std::unique_lock lock(
cimpl->__finishedMutex);
97 throw LocalException(
"You can only wait for toplevel states to finish!");
100 while (!
cimpl->__finished)
104 cimpl->__finishedCondition.wait(lock);
108 if (
cimpl->__finishedCondition.wait_for(lock, std::chrono::milliseconds(timeoutMs)) ==
109 std::cv_status::timeout)
111 throw LocalException(
"Statechart did not finish in time");
133 for (
auto& state : subStateList)
139 if (remote->getState() < eManagedIceObjectStarted)
141 ARMARX_INFO <<
"Waiting for remote state " << remote->getGlobalHierarchyString();
142 objectScheduler->waitForDependencies();
146 ARMARX_VERBOSE <<
"starting state " << stateName <<
" with parameters:\n"
151 if (tempInputParameters.size() > 0)
154 ->mapFromOutput(
"*",
"*")
155 ->_addSourceDictionary(eOutput, tempInputParameters)
156 ->setTargetDictToGreedy(greedyInputDictionary)
157 ->_applyMapping(inputParameters);
169 impl->triggeredEndstateEvent = ev;
170 ARMARX_DEBUG <<
"Substate finished with event " << ev->eventName;
179 STATEINFO <<
"Statemachine finished with event '" << ev->eventName <<
"' in state '"
181 <<
"The resulting output dictionary:\n"
207 const TransitionIceBase& t,
216 ARMARX_DEBUG <<
"Set parameters of parent " << stateName <<
" during transition: "
218 StringVariantContainerBaseMap srcSetOutputParams;
226 [&](
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,
245 StateBasePtr::dynamicCast(t.destinationState)->inputParameters,
246 StateBasePtr::dynamicCast(t.destinationState)->greedyInputDictionary);
248 std::string paramCheckOutput;
251 auto it =
cimpl->transitionFunctions.find(transitionId);
252 if (it !=
cimpl->transitionFunctions.end())
257 StateIceBasePtr::dynamicCast(t.destinationState),
258 StateIceBasePtr::dynamicCast(t.sourceState));
266 error.
infos.push_back(StateBasePtr::dynamicCast(t.destinationState)->stateName);
267 error.
infos.push_back(paramCheckOutput);
285 ARMARX_WARNING <<
"Could not execute transition!\nReturned reason: " +
289 <<
"\t- The input parameters of the destination state were not fully "
290 "set.\n\tDestination state: '"
291 << transitionError.
infos.at(0) <<
"'\n"
292 << transitionError.
infos.at(1);
298 ARMARX_WARNING <<
"Could not execute transition!\nReturned reason: " +
302 <<
"\t- The state '" << stateName <<
"' does not expect the event '"
303 <<
event->eventName <<
"' while in substate '"
304 << StateBasePtr::dynamicCast(activeSubstate)->stateName <<
"'.\n"
311 ARMARX_WARNING <<
"Could not execute transition!\nReturned reason: " +
323 STATEINFO <<
"TRANSITION due to Event '" <<
event->eventName <<
"' (EventReceiver: '"
324 <<
event->eventReceiverName <<
"')..." <<
flush;
325 boost::recursive_mutex::scoped_lock lock(
impl->__processEventMutex);
334 !StateIceBasePtr::dynamicCast(activeSubstate)->unbreakable &&
337 ARMARX_WARNING <<
"a parent state has events to process. this event will be skipped"
349 event, StateIceBasePtr::dynamicCast(activeSubstate), t, transitionError))
354 if (src && src->__hasSubstates() && src->__hasActiveSubstate())
356 ARMARX_INFO <<
"<<<< Trying to break substates of " << src->stateName <<
flush;
359 if (src->__breakActiveSubstate(event))
366 cimpl->__unbreakableBuffer.push(event);
369 ARMARX_INFO <<
"got event while in unbreakable substate: parent stateName:"
370 << stateName <<
flush;
383 t.sourceState = activeSubstate;
387 activeSubstate =
nullptr;
388 bool destinationStateInitialized =
389 StateBasePtr::dynamicCast(t.destinationState)->waitForInitialization();
391 << StateBasePtr::dynamicCast(t.destinationState)->stateName;
396 activeSubstate = t.destinationState;
397 if (t.sourceState && activeSubstate && !
cimpl->profilersDisabled)
401 localProfiler->logStatechartTransition(
403 StateIceBasePtr::dynamicCast(t.sourceState),
404 StateIceBasePtr::dynamicCast(t.destinationState),
406 localProfiler->logStatechartTransitionWithParameters(t);
411 StateControllerPtr::dynamicCast(t.destinationState)->_baseOnEnter();
414 if (src && src->unbreakable && src->eventsDelayed)
420 if (StateIceBasePtr::dynamicCast(t.destinationState)->unbreakable)
422 StateIceBasePtr::dynamicCast(t.destinationState)->eventsDelayed =
true;
447 <<
"The state '" << stateName
448 <<
"' does not have an activeSubstate and therefore cannot process any events."
455 const StateIceBasePtr& sourceState,
456 TransitionIceBase& resultTransition,
466 int selectedTransitionIndex = -1;
468 for (
unsigned int i = 0; i < transitions.size(); i++)
471 const TransitionIceBase& transition = transitions.at(i);
473 if ((transition.sourceState == sourceState || transition.fromAll)
474 && transition.evt->eventName == event->eventName
475 && (sourceState->stateName == event->eventReceiverName ||
479 selectedTransitionIndex = i;
485 if (selectedTransitionIndex == -1)
491 const TransitionIceBase& transition = transitions.at(selectedTransitionIndex);
493 resultTransition = transition;
501 const StateIceBasePtr& sourceState,
502 const StateIceBasePtr& destinationState)
const
507 if ((transition.sourceState != sourceState && !transition.fromAll)
508 || transition.evt->eventName != event->eventName
509 || (sourceState && sourceState->stateName != event->eventReceiverName &&
518 if (transition.sourceState && transition.fromAll)
526 PMPtr transitionMapping = PMPtr::dynamicCast(transition.mappingToNextStatesInput);
529 if (transitionMapping && sourceState)
532 const StringVariantContainerBaseMap parentSetInputParams =
534 const StringVariantContainerBaseMap srcSetOutputParams =
536 transitionMapping->setTargetDictToGreedy(
537 StateBasePtr::dynamicCast(transition.destinationState)->greedyInputDictionary);
538 StateParameterMap inputCopy;
540 transitionMapping->_addSourceDictionary(eParent, parentSetInputParams)
541 ->_addSourceDictionary(eOutput, srcSetOutputParams)
542 ->_addSourceDictionary(eEvent, event->properties)
543 ->_applyMapping(inputCopy);
544 std::string paramCheckOutput;
549 error.
infos.push_back(destinationState->stateName +
": " + paramCheckOutput);
561 return cimpl->__unbreakableBuffer.size();
568 cimpl->__eventBufferedDueToUnbreakableState = eventBuffered;
570 for (
unsigned int i = 0; i < subStateList.size(); ++i)
575 if (state->__getUnbreakableBufferSize() >
578 eventBuffered =
true;
581 StateControllerPtr::dynamicCast(subStateList.at(i))
582 ->__notifyEventBufferedDueToUnbreakableState(eventBuffered);
592 if (!ptr &&
impl->__parentState)
596 armarx::StateIceBase* base = newParentAsController.get();
597 *base = *
impl->__parentState;
598 ARMARX_WARNING <<
"Had to call factory manually for StateIceBase in "
599 <<
impl->__parentState->globalStateIdentifier;
600 return newParentAsController;
611 boost::recursive_mutex::scoped_lock lock(
impl->__processEventMutex);
615 result = StateControllerPtr::dynamicCast(activeSubstate)->_baseOnBreak(event);
619 activeSubstate =
nullptr;
633 <<
" in unbreakableBuffer - eventprocessorstate:" << stateName
634 <<
" - activeState: " << StateBasePtr::dynamicCast(activeSubstate)->stateName
636 impl->__eventUnbreakableBufferMutex.lock();
638 cimpl->__unbreakableBuffer.pop();
639 impl->__eventUnbreakableBufferMutex.unlock();
651 StateControllerPtr::dynamicCast(
__getParentState())->__processBufferedEvents();
659 for (
unsigned int i = 0; i < subStateList.size(); ++i)
661 RemoteStatePtr remoteStatePtr = RemoteStatePtr::dynamicCast(subStateList.at(i));
666 ARMARX_INFO <<
"Waiting for RemoteState " << remoteStatePtr->getName();
668 while (!(remoteStatePtr->getState() >= eManagedIceObjectStarted))
673 ARMARX_INFO <<
"RemoteState " << remoteStatePtr->getName() <<
"started.";
682 for (
unsigned int i = 0; i < transitions.size(); ++i)
685 if (transitions[i].evt->eventName == transition.evt->eventName &&
686 (transition.fromAll || transitions[i].sourceState == transition.sourceState) &&
687 transitions[i].destinationState == transition.destinationState)
708 if (
cimpl->__runningTask)
713 if (
cimpl->__runningTask->isRunning())
715 ARMARX_VERBOSE <<
"Waiting for running task of " << stateName <<
" to finish";
718 cimpl->__runningTask->stop();
720 catch (IceUtil::ThreadSyscallException& e)
725 eventsDelayed =
false;
728 ARMARX_DEBUG <<
"Resetting local and output parameters of state " << stateName;
733 std::string paramCheckOutput;
737 throw LocalException(
"Not all required inputparameters of the state '" + stateName +
738 "' are set:\n" + paramCheckOutput);
742 impl->cancelEnteringSubstates =
false;
744 impl->triggeredEndstateEvent =
nullptr;
746 impl->visitCounter++;
749 <<
"' (id: " <<
impl->localUniqueId <<
")" <<
flush;
750 IceUtil::Time executionStart = IceUtil::Time::now();
756 if (!
cimpl->profilersDisabled)
774 ARMARX_ERROR <<
"onEnter() of State " << globalStateIdentifier <<
" of class "
775 << stateClassName <<
" failed. Ignoring substates and sending Failure.\n"
785 if (!
cimpl->profilersDisabled)
794 IceUtil::Time duration = IceUtil::Time::now() - executionStart;
798 ARMARX_WARNING <<
"onEnter() of state '" + stateName +
"' took more than "
800 <<
" ms (In fact: " << duration.toMilliSeconds()
801 <<
" ms). The onEnter() method should not calculate complex operations."
810 if (!
impl->cancelEnteringSubstates)
814 impl->cancelEnteringSubstates =
false;
826 if (initialStateMapping)
828 PMPtr mapping = ParameterMappingPtr::dynamicCast(initialStateMapping);
829 mapping->_addSourceDictionary(eParent, combinedMap)
830 ->setTargetDictToGreedy(
831 StateBasePtr::dynamicCast(initState)->greedyInputDictionary)
832 ->_applyMapping(StateBasePtr::dynamicCast(initState)->inputParameters);
835 activeSubstate = initState;
837 if (!
cimpl->profilersDisabled && activeSubstate)
841 localProfiler->logStatechartTransition(
844 StateIceBasePtr::dynamicCast(activeSubstate),
845 "InitialTransition");
848 t.sourceState =
nullptr;
849 t.destinationState = activeSubstate;
850 EventBasePtr e(
new EventBase());
851 e->eventName =
"InitialTransition";
853 localProfiler->logStatechartTransitionWithParameters(t);
856 StateControllerPtr::dynamicCast(initState)->_baseOnEnter();
862 STATEINFO <<
"Entering substates of " << stateName <<
" has been canceled";
876 if (!
impl->__useRunFunction)
882 if (!
impl->__parentState ||
impl->__parentState->activeSubstate.get() ==
this)
886 cimpl->__runningTask->start();
907 <<
"run() of State " << globalStateIdentifier <<
" of class " << stateClassName
908 <<
" failed for unhandled reason. Ignoring substates and sending Failure\n"
925 impl->cancelEnteringSubstates =
true;
927 if (
cimpl->__runningTask)
929 cimpl->__runningTask->stop(
false);
941 "baseOnExit was called before substates were finished - baseOnBreak must be called "
942 "instead. ActiveSubstate: " +
943 StateBasePtr::dynamicCast(activeSubstate)->stateName);
950 IceUtil::Time executionStart = IceUtil::Time::now();
957 if (!
cimpl->profilersDisabled)
973 ARMARX_ERROR <<
"onExit() of State " << globalStateIdentifier <<
" of class "
974 << stateClassName <<
" failed\n"
978 IceUtil::Time duration = IceUtil::Time::now() - executionStart;
987 ARMARX_WARNING <<
"onExit() of state '" + stateName +
"' took more than "
989 <<
" ms). The onEnter() method should not calculate complex operations."
993 std::string paramCheckOutput;
997 throw LocalException(
"Not all required Outputparameters of state '" + stateName +
998 "' are set:\n" + paramCheckOutput);
1006 std::unique_lock lock(
cimpl->__finishedMutex);
1007 cimpl->__finished =
true;
1008 cimpl->__finishedCondition.notify_all();
1018 if (
cimpl->__runningTask)
1020 cimpl->__runningTask->stop(
false);
1034 eventsDelayed =
true;
1040 if (StateBasePtr::dynamicCast(activeSubstate)->unbreakable)
1043 StateBasePtr::dynamicCast(activeSubstate)->eventsDelayed =
true;
1056 <<
"' (id: " <<
impl->localUniqueId <<
")" <<
flush;
1058 if (
cimpl->__runningTask)
1060 cimpl->__runningTask->stop(
false);
1064 IceUtil::Time executionStart = IceUtil::Time::now();
1070 if (!
cimpl->profilersDisabled)
1085 ARMARX_ERROR <<
"onBreak() of State " << globalStateIdentifier <<
" of class "
1086 << stateClassName <<
" failed. Ignoring substates and sending Failure.\n"
1092 IceUtil::Time duration = IceUtil::Time::now() - executionStart;
1096 ARMARX_WARNING <<
"onEnter() of state '" + stateName +
"' took more than "
1098 <<
" ms (In fact: " << duration.toMilliSeconds()
1099 <<
" ms). The onEnter() method should not calculate complex operations."
1112 std::unique_lock lock(
cimpl->__finishedMutex);
1113 cimpl->__finished =
true;
1114 cimpl->__finishedCondition.notify_all();
1124 cimpl->localProfilers.insert(profiler);
1125 if (recursiveLevels < 0)
1129 else if (recursiveLevels == 0)
1133 else if (recursiveLevels > 0)
1138 for (AbstractStateIceBasePtr state : subStateList)
1142 if (stateController)
1144 stateController->addProfilerRecursive(profiler, recursiveLevels);
1153 cimpl->localProfilers.erase(profiler);
1154 if (recursiveLevels < 0)
1158 else if (recursiveLevels == 0)
1162 else if (recursiveLevels > 0)
1167 for (AbstractStateIceBasePtr state : subStateList)
1171 if (stateController)
1173 stateController->addProfilerRecursive(profiler, recursiveLevels);
1182 if (!
cimpl->__runningTask)
1187 return cimpl->__runningTask->isStopped();
1193 if (!
cimpl->__runningTask)
1197 return cimpl->__runningTask->isFinished();
1204 if (
cimpl->__runningTask)
1206 cimpl->__runningTask->join();
1213 cimpl->profilersDisabled = disable;
1220 ARMARX_INFO <<
"Regstering transition code for " << t.evt->eventName;
1230 (t.sourceState ? StateBasePtr::dynamicCast(t.sourceState)->stateName : std::string(
"")));
1235 const std::string sourceStateName)
const
1237 return sourceStateName +
":" + eventName;
1242 const std::string sourceStateName,
1243 TransitionIceBase& transition)
1246 for (
const TransitionIceBase& t : transitions)
1248 if (t.evt->eventName == eventName &&
1249 StateBasePtr::dynamicCast(t.sourceState)->stateName == sourceStateName)
1255 ARMARX_INFO <<
"Could not find transition in " << transitions.size() <<
" transitions";
1273 return !
cimpl->__eventBufferedDueToUnbreakableState;
1280 for (
unsigned int i = 0; i < subStateList.size(); i++)
1282 StateControllerPtr::dynamicCast(subStateList[i])->disableRunFunction();
1286 impl->__useRunFunction =
false;
1288 if (
cimpl->__runningTask &&
cimpl->__runningTask->isRunning())
1290 ARMARX_VERBOSE <<
"State with name '" << stateName <<
"' is waiting for the RunFunction";
1291 cimpl->__runningTask->stop();
1294 cimpl->__runningTask =
nullptr;
#define MAX_USER_CODE_DURATION
Base Class for all Logging classes.
virtual void run()
Virtual function, that can be reimplemented to calculate complex operations.
virtual bool isInitialized() const
Returns the status of this state. Only if a state is initialized, it can be used.
virtual bool __hasActiveSubstate()
Virtual function to indicate wheter a state has an active substate or not. To be overridden by Remote...
void __checkPhase(StatePhase allowedType, const char *functionName) const
Helper function for checking if a function was called in valid position of the statechart.
virtual void onBreak()
Virtual function, in which the behaviour of state is defined, when it is abnormally exited....
void setStatePhase(StatePhase newPhase)
virtual StateParameterMap & getOutputParameters()
void __copyDefaultValuesToInput()
virtual void onEnter()
Virtual function, in which the behaviour of state is defined, when it is entered. Can be overridden,...
std::string getLocalHierarchyString() const
Function to get a string that contains als parent states and this state. (e.g. "Robot->Functional->Id...
std::unique_ptr< Impl > impl
StatePhase getStatePhase() const
std::string getGlobalHierarchyString() const
virtual bool __hasSubstates()
Virtual function to indicate wheter a state has substates or not. To be overridden by RemoteState to ...
StringVariantContainerBaseMap __getSetInputAndLocalParameters() const
Combines both maps to one map and returns a new map of only the set parameters.
std::string getStateName() const
getStateName
virtual void onExit()
Virtual function, in which the behaviour of state is defined, when it is exited. Can be overridden,...
virtual void __notifyEventBufferedDueToUnbreakableState(bool eventBuffered=true)
void __printTransitionError(const TransitionError &transitionError, const EventPtr &event) const
virtual void __enqueueEvent(const EventPtr event)
std::function< void(StateController *state, const StateIceBasePtr &nextState, const StateIceBasePtr &previousState)> transitionFunction
virtual bool __getUnbreakableBufferStati() const
Before:Function to get the unbreakable-buffer status of all parent state - recursively.
std::string getTransitionID(const TransitionIceBase &t) const
virtual bool __breakActiveSubstate(const EventPtr event)
virtual void _baseOnExit()
Called by StateController::processEvent()-function or parentstate. Must NOT be called by user.
bool findTransition(const std::string &eventName, const std::string sourceStateName, TransitionIceBase &transition)
virtual void _removeInstalledConditions()
This function is implemented in StateUtil and removes all conditions that have been installed in onEn...
virtual void __finalize(const EventPtr event)
Function that gets called, when a state enters a FinalSubstate. Virtual function, so that RemoteState...
virtual void __processBufferedEvents()
Processes buffered events, that could not be processed immediately due to unbreakable substates.
void disableStateReporting(bool disable=true)
Disables the reporting to profilers for this states during state visits.
virtual void __processEvent(const EventPtr event, bool buffered=false)
Main function to control the statemachine/state.
std::unique_ptr< Impl > cimpl
bool isRunningTaskStopped() const
isRunningTaskStopped checks whether the RunningTask, that executes run() is requested to stop.
void waitForStateToFinish(int timeoutMs=-1) const
waitForStateToFinish waits until this thread has finished (i.e.
void enter(const StringVariantContainerBaseMap &tempInputParameters=StringVariantContainerBaseMap())
Function to set the statemachine in the first state and call OnEnter().
virtual unsigned int __getUnbreakableBufferSize() const
TransitionError __validateTransition(const TransitionIceBase &transition, const EventPtr event, const StateIceBasePtr &sourceState, const StateIceBasePtr &destinationState) const
void waitForRunningTaskToFinish() const
Waits until the run-function has finished.
bool __findValidTransition(const EventPtr &event, const StateIceBasePtr &sourceState, TransitionIceBase &resultTransition, TransitionError &error) const
void __waitForRemoteStates() const
void disableRunFunction()
disableRunFunction sets useRunFunction to false and waits (blocking) for the current StateBase::run()...
virtual void _baseOnEnter()
Called by StateControllerprocessEvent()-function or parentstate.
bool __applyMappings(const StateControllerPtr &srcState, const TransitionIceBase &t, const EventPtr &event, TransitionError &error)
Apply the mappings during a transitions.
~StateController() override
StateControllerPtr __getParentState() const
Getter function that automatically casts the parentState member of StateBase into StateControllerPtr.
void addProfilerRecursive(Profiler::ProfilerPtr profiler, int recursiveLevels=0)
addProfilerRecursive recursively adds a new armarx::Profiler::Profiler object as armarx::StateControl...
bool __checkExistenceOfTransition(const TransitionIceBase &transition)
bool isRunningTaskFinished() const
Checks whether the run() function has already finished.
void removeProfilerRecursive(Profiler::ProfilerPtr profiler, int recursiveLevels=0)
virtual bool _baseOnBreak(const EventPtr evt)
Called by StateControllerprocessEvent()-function or parentstate. Must NOT be called by user.
void addTransitionFunction(const TransitionIceBase &t, transitionFunction function)
virtual void __substatesFinished(const EventPtr ev)
Function that gets called, when a state enters a FinalSubstate. Virtual function, so that RemoteState...
static IceInternal::Handle< IceGeneratedState > createInstance(std::string stateName="")
#define ARMARX_CHECK_EXPRESSION(expression)
This macro evaluates the expression and if it turns out to be false it will throw an ExpressionExcept...
#define ARMARX_INFO
The normal logging level.
#define ARMARX_ERROR_S
The logging level for unexpected behaviour, that must be fixed.
#define ARMARX_ERROR
The logging level for unexpected behaviour, that must be fixed.
#define ARMARX_DEBUG
The logging level for output that is only interesting while debugging.
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
#define ARMARX_VERBOSE
The logging level for verbose information.
boost::unique_lock< HiddenTimedMutex > ScopedLock
std::shared_ptr< Profiler > ProfilerPtr
void copyDictionary(const StringVariantContainerBaseMap &source, StringVariantContainerBaseMap &destination)
Clears the destination map and copies the parameters of the source in it.
std::string transitionErrorToString(TransitionErrorType type)
std::string getDictionaryString(const StringVariantContainerBaseMap &mymap)
Converts the map into a string-representation.
bool checkForCompleteParameters(const StateParameterMap ¶mMap, std::string *logOutput=nullptr)
void unsetParameters(StateParameterMap ¶mMap)
Sets all entries of the given dictionary to the stored default values.
StringVariantContainerBaseMap getSetValues(const StateParameterMap ¶mMap)
This file offers overloads of toIce() and fromIce() functions for STL container types.
IceInternal::Handle< RemoteState > RemoteStatePtr
@ eTransitionErrorUnexpectedEvent
@ eTransitionErrorUndefined
IceUtil::Handle< ArmarXObjectScheduler > ArmarXObjectSchedulerPtr
std::string GetHandledExceptionString()
ParameterMappingPtr PMPtr
const LogSender::manipulator flush
IceInternal::Handle< Event > EventPtr
Typedef of EventPtr as IceInternal::Handle<Event> for convenience.
IceInternal::Handle< StateController > StateControllerPtr
ParameterMappingPtr createMapping()
Returns a new and empty instance of ParameterMapping.
std::vector< std::string > infos
vector to store info data in
TransitionErrorType errorType