StateController.cpp
Go to the documentation of this file.
1/*
2* This file is part of ArmarX.
3*
4* Copyright (C) 2011-2016, High Performance Humanoid Technologies (H2T), Karlsruhe Institute of Technology (KIT), all rights reserved.
5*
6* ArmarX is free software; you can redistribute it and/or modify
7* it under the terms of the GNU General Public License version 2 as
8* published by the Free Software Foundation.
9*
10* ArmarX is distributed in the hope that it will be useful, but
11* WITHOUT ANY WARRANTY; without even the implied warranty of
12* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13* GNU General Public License for more details.
14*
15* You should have received a copy of the GNU General Public License
16* along with this program. If not, see <http://www.gnu.org/licenses/>.
17*
18* @package ArmarX::statechart
19* @author Mirko Waechter ( mirko.waechter at kit dot edu)
20* @date 2012
21* @copyright http://www.gnu.org/licenses/gpl-2.0.txt
22* GNU General Public License
23*/
24
25#include "StateController.h"
26
27#include <IceUtil/Time.h>
28
31#include <ArmarXCore/core/services/profiler/Profiler.h> // for ProfilerPtr
35
36#include "RemoteState.h"
37#include "StateBaseImpl.h"
38#include "StateControllerImpl.h"
39#include "StatechartManager.h"
41
42using namespace armarx;
43using namespace StateUtilFunctions;
44
45
46#define MAX_USER_CODE_DURATION 100 //milliseconds
47
51
53 IceUtil::Shared(source),
54 Ice::Object(source),
55 StateIceBase(source),
56 Logging(source),
57 StateBase(source),
58 cimpl(new Impl)
59{
60 cimpl->transitionFunctions = source.cimpl->transitionFunctions;
61 cimpl->localProfilers = source.cimpl->localProfilers;
62 cimpl->profilersDisabled = source.cimpl->profilersDisabled;
63}
64
66{
67 // ARMARX_INFO << "~StateController "<< stateClassName << " " << tag.tagName << " " << (long)this /*<< "\n" << LogSender::createBackTrace() */<< std::endl;
68 if (cimpl->__runningTask)
69 {
70 cimpl->__runningTask->stop();
71 }
72
73 {
74 std::unique_lock lock(cimpl->__finishedMutex);
75 cimpl->__finished = true;
76 }
77
78 cimpl->__finishedCondition.notify_all();
79}
80
81bool
83{
84 std::unique_lock lock(cimpl->__finishedMutex);
85 return cimpl->__finished;
86}
87
88void
90{
92 ARMARX_INFO << "Waiting for state to finish";
93 std::unique_lock lock(cimpl->__finishedMutex);
94
95 if (__getParentState())
96 {
97 throw LocalException("You can only wait for toplevel states to finish!");
98 }
99
100 while (!cimpl->__finished)
101 {
102 if (timeoutMs == -1)
103 {
104 cimpl->__finishedCondition.wait(lock);
105 }
106 else
107 {
108 if (cimpl->__finishedCondition.wait_for(lock, std::chrono::milliseconds(timeoutMs)) ==
109 std::cv_status::timeout)
110 {
111 throw LocalException("Statechart did not finish in time");
112 }
113 }
114 }
115}
116
117void
118StateController::enter(const StringVariantContainerBaseMap& tempInputParameters)
119{
121 __checkPhase(eDefined, __PRETTY_FUNCTION__);
122
123 if (!isInitialized())
124 {
126 }
127
128 if (__hasSubstates() && !initState._ptr)
129 {
130 throw exceptions::local::eNullPointerException("initState was not set!");
131 }
132
133 for (auto& state : subStateList)
134 {
135 RemoteStatePtr remote = RemoteStatePtr::dynamicCast(state);
136 if (remote)
137 {
138 ArmarXObjectSchedulerPtr objectScheduler = remote->getObjectScheduler();
139 if (remote->getState() < eManagedIceObjectStarted)
140 {
141 ARMARX_INFO << "Waiting for remote state " << remote->getGlobalHierarchyString();
142 objectScheduler->waitForDependencies();
143 }
144 }
145 }
146 ARMARX_VERBOSE << "starting state " << stateName << " with parameters:\n"
147 << StateUtilFunctions::getDictionaryString(tempInputParameters) << flush;
148
150 //fill the inputDictionary of the entry state
151 if (tempInputParameters.size() > 0)
152 {
154 ->mapFromOutput("*", "*")
155 ->_addSourceDictionary(eOutput, tempInputParameters)
156 ->setTargetDictToGreedy(greedyInputDictionary)
157 ->_applyMapping(inputParameters);
158 }
159
160 _baseOnEnter();
161}
162
163void
165{
167 activeSubstate =
168 nullptr; // line needed or not?! Pro: Finalstate will be broken on leaving of parentstate - Contra: baseOnExit() of finalstate wont be called.
169 impl->triggeredEndstateEvent = ev;
170 ARMARX_DEBUG << "Substate finished with event " << ev->eventName;
171 if (__getParentState() && StateControllerPtr::dynamicCast(__getParentState()))
172 {
173 __finalize(ev);
174 }
175 else
176 {
178 _baseOnExit();
179 STATEINFO << "Statemachine finished with event '" << ev->eventName << "' in state '"
180 << getGlobalHierarchyString() << "'\n"
181 << "The resulting output dictionary:\n"
183 }
184}
185
186// in function, so that it can be overridden in remoteStateWrapper
187void
189{
191 __enqueueEvent(event);
192}
193
194void
196{
198 if (__getParentState())
199 {
201 impl->manager->addEvent(event, __getParentState());
202 }
203}
204
205bool
207 const TransitionIceBase& t,
208 const EventPtr& event,
209 TransitionError& error)
210{
212 srcState->setStatePhase(eDefined);
213
214 // get source dictionaries and apply the mapping
215 const StringVariantContainerBaseMap parentSetInputParams = __getSetInputAndLocalParameters();
216 ARMARX_DEBUG << "Set parameters of parent " << stateName << " during transition: "
217 << StateUtilFunctions::getDictionaryString(parentSetInputParams);
218 StringVariantContainerBaseMap srcSetOutputParams;
219
220 if (srcState)
221 {
222 srcSetOutputParams = StateUtilFunctions::getSetValues(srcState->getOutputParameters());
223 }
224
225 auto applyMapping =
226 [&](const ParameterMappingIceBasePtr& icepm, StateParameterMap& targetMap, bool greedy)
227 {
229 PMPtr pm = PMPtr::dynamicCast(icepm);
230
231 if (pm)
232 {
233 pm->setTargetDictToGreedy(greedy);
234
235 // ARMARX_DEBUG_S << "The following output parameters of source state " << srcState->getStateName() << " are available: " << StateUtilFunctions::getDictionaryString(srcSetOutputParams);
236 pm->_addSourceDictionary(eParent, parentSetInputParams)
237 ->_addSourceDictionary(eOutput, srcSetOutputParams)
238 ->_addSourceDictionary(eEvent, event->properties)
239 ->_applyMapping(targetMap);
240 }
241 };
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);
247 // ARMARX_DEBUG << "The following input parameters from parent state " << getStateName() << " are available: " << StateUtilFunctions::getDictionaryString(inputParameters);
248 std::string paramCheckOutput;
249
250 auto transitionId = getTransitionID(t);
251 auto it = cimpl->transitionFunctions.find(transitionId);
252 if (it != cimpl->transitionFunctions.end())
253 {
255 transitionFunction& f = it->second;
256 f(this,
257 StateIceBasePtr::dynamicCast(t.destinationState),
258 StateIceBasePtr::dynamicCast(t.sourceState));
259 }
260
261 if (!checkForCompleteParameters(StateBasePtr::dynamicCast(t.destinationState)->inputParameters,
262 &paramCheckOutput))
263 {
266 error.infos.push_back(StateBasePtr::dynamicCast(t.destinationState)->stateName);
267 error.infos.push_back(paramCheckOutput);
268 __printTransitionError(error, event);
269 return false;
270 }
271
272
273 return true;
274}
275
276void
278 const EventPtr& event) const
279{
281 switch (transitionError.errorType)
282 {
284 {
285 ARMARX_WARNING << "Could not execute transition!\nReturned reason: " +
287 transitionError.errorType) +
288 "\nExplanations:\n"
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);
293 }
294 break;
295
297 {
298 ARMARX_WARNING << "Could not execute transition!\nReturned reason: " +
300 transitionError.errorType) +
301 "\nExplanations:\n"
302 << "\t- The state '" << stateName << "' does not expect the event '"
303 << event->eventName << "' while in substate '"
304 << StateBasePtr::dynamicCast(activeSubstate)->stateName << "'.\n"
305 << flush;
306 }
307 break;
308
309 default:
310 {
311 ARMARX_WARNING << "Could not execute transition!\nReturned reason: " +
313 transitionError.errorType)
314 << flush;
315 }
316 }
317}
318
319void
320StateController::__processEvent(const EventPtr event, bool buffered)
321{
323 STATEINFO << "TRANSITION due to Event '" << event->eventName << "' (EventReceiver: '"
324 << event->eventReceiverName << "')..." << flush;
325 boost::recursive_mutex::scoped_lock lock(impl->__processEventMutex);
326
327 if (!isInitialized())
328 {
330 }
331
332 //check if the current state is breakable and if any parent state has events to process
333 if (!buffered && __hasActiveSubstate() &&
334 !StateIceBasePtr::dynamicCast(activeSubstate)->unbreakable &&
336 {
337 ARMARX_WARNING << "a parent state has events to process. this event will be skipped"
338 << flush;
339 return;
340 }
342
343 TransitionError transitionError;
344 transitionError.errorType = eTransitionErrorUndefined;
345
346 TransitionIceBase t;
347
349 event, StateIceBasePtr::dynamicCast(activeSubstate), t, transitionError))
350 {
352 StateControllerPtr src = StateControllerPtr::dynamicCast(activeSubstate);
353
354 if (src && src->__hasSubstates() && src->__hasActiveSubstate())
355 {
356 ARMARX_INFO << "<<<< Trying to break substates of " << src->stateName << flush;
357
358 // Check if the active substate is unbreakable
359 if (src->__breakActiveSubstate(event))
360 {
361 src->_baseOnExit();
362 }
363 else // substate of src unbreakable
364 {
365 HiddenTimedMutex::ScopedLock lock(impl->__eventUnbreakableBufferMutex);
366 cimpl->__unbreakableBuffer.push(event);
368
369 ARMARX_INFO << "got event while in unbreakable substate: parent stateName:"
370 << stateName << flush;
371 return;
372 }
373 }
374 else if (src)
375 {
376 src->_baseOnExit();
377 }
378
379 if (t.fromAll)
380 {
381 // required by StatechartLogger
382 // source state is empty if event is received as fromAll
383 t.sourceState = activeSubstate;
384 }
385
386 // set to NULL in case mapping could not be applied, so onExit wont be called twice
387 activeSubstate = nullptr;
388 bool destinationStateInitialized =
389 StateBasePtr::dynamicCast(t.destinationState)->waitForInitialization();
390 ARMARX_CHECK_EXPRESSION(destinationStateInitialized)
391 << StateBasePtr::dynamicCast(t.destinationState)->stateName;
392
393 if (__applyMappings(src, t, event, transitionError))
394 {
396 activeSubstate = t.destinationState;
397 if (t.sourceState && activeSubstate && !cimpl->profilersDisabled)
398 {
399 for (const Profiler::ProfilerPtr& localProfiler : cimpl->localProfilers)
400 {
401 localProfiler->logStatechartTransition(
403 StateIceBasePtr::dynamicCast(t.sourceState),
404 StateIceBasePtr::dynamicCast(t.destinationState),
405 t.evt->eventName);
406 localProfiler->logStatechartTransitionWithParameters(t);
407 }
408 }
409
410
411 StateControllerPtr::dynamicCast(t.destinationState)->_baseOnEnter();
412
413 //check if unbreakable state has caused a lock
414 if (src && src->unbreakable && src->eventsDelayed)
415 {
418 {
419 // check if next state is unbreakable as well
420 if (StateIceBasePtr::dynamicCast(t.destinationState)->unbreakable)
421 {
422 StateIceBasePtr::dynamicCast(t.destinationState)->eventsDelayed = true;
423 }
424 else
425 {
426 __getParentState()->__processBufferedEvents();
427 }
428 }
429 }
430
431
432 // Transition done
433 return;
434 }
435 }
437
438 //no fitting transition found->throw
439 if (activeSubstate)
440 {
441 __printTransitionError(transitionError, event);
442 //throw eUnexpectedEvent(event, activeSubstate);
443 }
444 else
445 {
447 << "The state '" << stateName
448 << "' does not have an activeSubstate and therefore cannot process any events."
449 << flush;
450 }
451}
452
453bool
455 const StateIceBasePtr& sourceState,
456 TransitionIceBase& resultTransition,
457 TransitionError& error) const
458{
460 // loop through transition table to find the correct transition fitting to the event, activeState and DestinationState
461
463
464 // first check if there is a transition fitting to the input
465
466 int selectedTransitionIndex = -1;
467
468 for (unsigned int i = 0; i < transitions.size(); i++)
469 {
471 const TransitionIceBase& transition = transitions.at(i);
472
473 if ((transition.sourceState == sourceState || transition.fromAll) // correct source state
474 && transition.evt->eventName == event->eventName // correct event
475 && (sourceState->stateName == event->eventReceiverName ||
476 event->eventReceiverName == EVENTTOALL) // correct eventreceiver
477 )
478 {
479 selectedTransitionIndex = i;
480 break;
481 }
482 }
484
485 if (selectedTransitionIndex == -1)
486 {
488 return false;
489 }
490
491 const TransitionIceBase& transition = transitions.at(selectedTransitionIndex);
492
493 resultTransition = transition;
495 return true;
496}
497
499StateController::__validateTransition(const TransitionIceBase& transition,
500 const EventPtr event,
501 const StateIceBasePtr& sourceState,
502 const StateIceBasePtr& destinationState) const
503{
505 TransitionError error;
506
507 if ((transition.sourceState != sourceState && !transition.fromAll) // wrong source state
508 || transition.evt->eventName != event->eventName // wrong event
509 || (sourceState && sourceState->stateName != event->eventReceiverName &&
510 event->eventReceiverName != EVENTTOALL) // wrong eventreceiver
511 )
512 {
514 return error;
515 }
516
517
518 if (transition.sourceState && transition.fromAll) // this should never happen
519 {
521 return error;
522 }
523
524
525 // get source dictionaries and apply the mapping
526 PMPtr transitionMapping = PMPtr::dynamicCast(transition.mappingToNextStatesInput);
527
529 if (transitionMapping && sourceState)
530 {
532 const StringVariantContainerBaseMap parentSetInputParams =
534 const StringVariantContainerBaseMap srcSetOutputParams =
535 StateUtilFunctions::getSetValues(sourceState->outputParameters);
536 transitionMapping->setTargetDictToGreedy(
537 StateBasePtr::dynamicCast(transition.destinationState)->greedyInputDictionary);
538 StateParameterMap inputCopy;
539 StateUtilFunctions::copyDictionary(destinationState->inputParameters, inputCopy);
540 transitionMapping->_addSourceDictionary(eParent, parentSetInputParams)
541 ->_addSourceDictionary(eOutput, srcSetOutputParams)
542 ->_addSourceDictionary(eEvent, event->properties)
543 ->_applyMapping(inputCopy);
544 std::string paramCheckOutput;
545
546 if (!checkForCompleteParameters(inputCopy, &paramCheckOutput))
547 {
549 error.infos.push_back(destinationState->stateName + ": " + paramCheckOutput);
550 return error;
551 }
552 }
553
555 return error;
556}
557
558unsigned int
560{
561 return cimpl->__unbreakableBuffer.size();
562}
563
564void
566{
568 cimpl->__eventBufferedDueToUnbreakableState = eventBuffered;
569
570 for (unsigned int i = 0; i < subStateList.size(); ++i)
571 {
573 StateControllerPtr state = StateControllerPtr::dynamicCast(subStateList.at(i));
574
575 if (state->__getUnbreakableBufferSize() >
576 0) // check if an event in this state is buffered. if it is, the eventBuffered must be set to true, otherwise dont change it
577 {
578 eventBuffered = true;
579 }
580
581 StateControllerPtr::dynamicCast(subStateList.at(i))
582 ->__notifyEventBufferedDueToUnbreakableState(eventBuffered);
583 }
584}
585
588{
590 StateControllerPtr ptr = StateControllerPtr::dynamicCast(impl->__parentState);
591
592 if (!ptr && impl->__parentState)
593 {
594 // We can try to call the factory
595 auto newParentAsController = IceGeneratedState::createInstance();
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;
601 }
602
603 return ptr;
604}
605
606bool
608{
610 bool result = true;
611 boost::recursive_mutex::scoped_lock lock(impl->__processEventMutex);
612
614 {
615 result = StateControllerPtr::dynamicCast(activeSubstate)->_baseOnBreak(event);
616
617 if (result) //active substate is broken -> set to NULL
618 {
619 activeSubstate = nullptr;
620 }
621 }
622
623 return result;
624}
625
626void
628{
630 while (__getUnbreakableBufferSize() > 0)
631 {
632 ARMARX_VERBOSE << "processing " << cimpl->__unbreakableBuffer.front()->eventName
633 << " in unbreakableBuffer - eventprocessorstate:" << stateName
634 << " - activeState: " << StateBasePtr::dynamicCast(activeSubstate)->stateName
635 << flush;
636 impl->__eventUnbreakableBufferMutex.lock();
637 EventPtr event = cimpl->__unbreakableBuffer.front();
638 cimpl->__unbreakableBuffer.pop();
639 impl->__eventUnbreakableBufferMutex.unlock();
640 __processEvent(event, true);
641
643 {
646 }
647 }
648
649 if (__getParentState())
650 {
651 StateControllerPtr::dynamicCast(__getParentState())->__processBufferedEvents();
652 }
653}
654
655void
657{
659 for (unsigned int i = 0; i < subStateList.size(); ++i)
660 {
661 RemoteStatePtr remoteStatePtr = RemoteStatePtr::dynamicCast(subStateList.at(i));
662
663 if (remoteStatePtr)
664 {
666 ARMARX_INFO << "Waiting for RemoteState " << remoteStatePtr->getName();
667
668 while (!(remoteStatePtr->getState() >= eManagedIceObjectStarted))
669 {
670 usleep(10);
671 }
672
673 ARMARX_INFO << "RemoteState " << remoteStatePtr->getName() << "started.";
674 break;
675 }
676 }
677}
678
679bool
680StateController::__checkExistenceOfTransition(const TransitionIceBase& transition)
681{
682 for (unsigned int i = 0; i < transitions.size(); ++i)
683 {
685 if (transitions[i].evt->eventName == transition.evt->eventName &&
686 (transition.fromAll || transitions[i].sourceState == transition.sourceState) &&
687 transitions[i].destinationState == transition.destinationState)
688 {
689 return true;
690 }
691 }
692
693 return false;
694}
695
696void
698{
700 if (!isInitialized())
701 {
703 }
704
705 {
706 HiddenTimedMutex::ScopedLock lock(impl->__stateMutex);
707
708 if (cimpl->__runningTask)
709 {
711 try
712 {
713 if (cimpl->__runningTask->isRunning())
714 {
715 ARMARX_VERBOSE << "Waiting for running task of " << stateName << " to finish";
716 }
717
718 cimpl->__runningTask->stop(); // this blocks until the thread has finished
719 }
720 catch (IceUtil::ThreadSyscallException& e)
721 {
722 }
723 }
724
725 eventsDelayed = false;
726
727 //reset local & output parameters, so that they are NOT set in case we have been in this state before
728 ARMARX_DEBUG << "Resetting local and output parameters of state " << stateName;
730 StateUtilFunctions::unsetParameters(outputParameters);
731
732 // std::ARMARX_INFO << StateUtil::getDictionaryString(inputParameters);
733 std::string paramCheckOutput;
734
735 if (!checkForCompleteParameters(inputParameters, &paramCheckOutput))
736 {
737 throw LocalException("Not all required inputparameters of the state '" + stateName +
738 "' are set:\n" + paramCheckOutput);
739 }
740
741 // overwrite status of last visit
742 impl->cancelEnteringSubstates = false;
743
744 impl->triggeredEndstateEvent = nullptr;
745
746 impl->visitCounter++;
747
748 STATEINFO << "Entering State '" << getLocalHierarchyString()
749 << "' (id: " << impl->localUniqueId << ")" << flush;
750 IceUtil::Time executionStart = IceUtil::Time::now();
751
752 try
753 {
756 if (!cimpl->profilersDisabled)
757 {
758 for (const Profiler::ProfilerPtr& localProfiler : cimpl->localProfilers)
759 {
760 localProfiler->logEvent(Profiler::Profiler::EventType::eFunctionStart,
762 getStateName());
763 localProfiler->logStatechartInputParameters(getGlobalHierarchyString(),
764 inputParameters);
765 }
766 }
767 onEnter();
768 }
770 {
771 }
772 catch (...)
773 {
774 ARMARX_ERROR << "onEnter() of State " << globalStateIdentifier << " of class "
775 << stateClassName << " failed. Ignoring substates and sending Failure.\n"
777
778 lock.unlock();
780 __enqueueEvent(new Failure(stateName));
781 return;
782 }
783
785 if (!cimpl->profilersDisabled)
786 {
787 for (const Profiler::ProfilerPtr& localProfiler : cimpl->localProfilers)
788 {
789 localProfiler->logStatechartLocalParameters(getGlobalHierarchyString(),
790 localParameters);
791 }
792 }
793
794 IceUtil::Time duration = IceUtil::Time::now() - executionStart;
795
796 if (duration.toMilliSeconds() > MAX_USER_CODE_DURATION)
797 {
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."
802 << flush;
803 }
804 }
805
806 {
808 HiddenTimedMutex::ScopedLock lock(impl->__stateMutex);
809
810 if (!impl->cancelEnteringSubstates)
811 {
812 if (__hasSubstates())
813 {
814 impl->cancelEnteringSubstates = false;
815
816 if (!initState)
817 {
818 ARMARX_WARNING << "No initial substate set in '" << stateName << "'" << flush;
819 }
820 else
821 {
822 StringVariantContainerBaseMap combinedMap = __getSetInputAndLocalParameters();
823 ARMARX_DEBUG << "Source for initial state: "
825
826 if (initialStateMapping)
827 {
828 PMPtr mapping = ParameterMappingPtr::dynamicCast(initialStateMapping);
829 mapping->_addSourceDictionary(eParent, combinedMap)
830 ->setTargetDictToGreedy(
831 StateBasePtr::dynamicCast(initState)->greedyInputDictionary)
832 ->_applyMapping(StateBasePtr::dynamicCast(initState)->inputParameters);
833 }
834
835 activeSubstate = initState;
836 lock.unlock();
837 if (!cimpl->profilersDisabled && activeSubstate)
838 {
839 for (const Profiler::ProfilerPtr& localProfiler : cimpl->localProfilers)
840 {
841 localProfiler->logStatechartTransition(
843 nullptr,
844 StateIceBasePtr::dynamicCast(activeSubstate),
845 "InitialTransition");
846
847 TransitionIceBase t;
848 t.sourceState = nullptr;
849 t.destinationState = activeSubstate;
850 EventBasePtr e(new EventBase());
851 e->eventName = "InitialTransition";
852 t.evt = e;
853 localProfiler->logStatechartTransitionWithParameters(t);
854 }
855 }
856 StateControllerPtr::dynamicCast(initState)->_baseOnEnter();
857 }
858 }
859 }
860 else
861 {
862 STATEINFO << "Entering substates of " << stateName << " has been canceled";
863 }
864 }
865
866 // triggers to run the run()-Function in a separate thread
867 _startRun();
868}
869
870void
872{
874 HiddenTimedMutex::ScopedLock lock(impl->__stateMutex);
875
876 if (!impl->__useRunFunction)
877 {
878 return;
879 }
880
881 // only execute if still active state and not changed to another state in onEnter()
882 if (!impl->__parentState || impl->__parentState->activeSubstate.get() == this)
883 {
884 cimpl->__runningTask = new RunningTask<StateController>(
885 this, &StateController::_baseRun, stateClassName + "RunningTask");
886 cimpl->__runningTask->start();
887 }
888}
889
890void
892{
894 try
895 {
896 run();
897 }
899 {
900 }
901 catch (...)
902 {
904 if (getStatePhase() == eEntered)
905 {
907 << "run() of State " << globalStateIdentifier << " of class " << stateClassName
908 << " failed for unhandled reason. Ignoring substates and sending Failure\n"
910
911 __enqueueEvent(new Failure(stateName));
912 }
913
914 return;
915 }
916}
917
918void
920{
922 HiddenTimedMutex::ScopedLock lock(impl->__stateMutex);
923
924 // substates must not be entered if onexit has already been called
925 impl->cancelEnteringSubstates = true;
926
927 if (cimpl->__runningTask)
928 {
929 cimpl->__runningTask->stop(false);
930 //runningTask = NULL;
931 }
932
933 if (!isInitialized())
934 {
936 }
937
939 {
941 "baseOnExit was called before substates were finished - baseOnBreak must be called "
942 "instead. ActiveSubstate: " +
943 StateBasePtr::dynamicCast(activeSubstate)->stateName);
944 }
945
946
947 STATEINFO << "Leaving State '" << getLocalHierarchyString() << "' (id: " << impl->localUniqueId
948 << ")" << flush;
949
950 IceUtil::Time executionStart = IceUtil::Time::now();
951
952 try
953 {
956 onExit();
957 if (!cimpl->profilersDisabled)
958 {
959 for (const Profiler::ProfilerPtr& localProfiler : cimpl->localProfilers)
960 {
963 getStateName());
964 localProfiler->logStatechartLocalParameters(getGlobalHierarchyString(),
965 localParameters);
966 localProfiler->logStatechartOutputParameters(getGlobalHierarchyString(),
967 outputParameters);
968 }
969 }
970 }
971 catch (...)
972 {
973 ARMARX_ERROR << "onExit() of State " << globalStateIdentifier << " of class "
974 << stateClassName << " failed\n"
976 }
977
978 IceUtil::Time duration = IceUtil::Time::now() - executionStart;
980
981
983
984
985 if (duration.toMilliSeconds() > MAX_USER_CODE_DURATION)
986 {
987 ARMARX_WARNING << "onExit() of state '" + stateName + "' took more than "
988 << MAX_USER_CODE_DURATION << " ms (In fact: " << duration.toMilliSeconds()
989 << " ms). The onEnter() method should not calculate complex operations."
990 << flush;
991 }
992
993 std::string paramCheckOutput;
994
995 if (!checkForCompleteParameters(getOutputParameters(), &paramCheckOutput))
996 {
997 throw LocalException("Not all required Outputparameters of state '" + stateName +
998 "' are set:\n" + paramCheckOutput);
999 }
1000
1001 //reset input parameters, so that they are NOT set when we reenter this state
1002 StateUtilFunctions::unsetParameters(inputParameters);
1004
1005 {
1006 std::unique_lock lock(cimpl->__finishedMutex);
1007 cimpl->__finished = true;
1008 cimpl->__finishedCondition.notify_all();
1009 }
1010}
1011
1012bool
1014{
1016 HiddenTimedMutex::ScopedLock lock(impl->__stateMutex);
1017
1018 if (cimpl->__runningTask)
1019 {
1020 cimpl->__runningTask->stop(false);
1021 //runningTask = NULL;
1022 }
1023
1024 if (!isInitialized())
1025 {
1027 }
1028
1029
1030 bool result = true;
1031
1032 if (unbreakable)
1033 {
1034 eventsDelayed = true;
1035 result = false;
1036 }
1037 else if (subStateList.size() > 0 && __hasActiveSubstate())
1038 {
1039 // Check if the active substate is unbreakable
1040 if (StateBasePtr::dynamicCast(activeSubstate)->unbreakable)
1041 {
1042 // signal to upper states that this state cannot be broken
1043 StateBasePtr::dynamicCast(activeSubstate)->eventsDelayed = true;
1044 result = false;
1045 }
1046 else
1047 {
1048 result = __breakActiveSubstate(evt);
1049 }
1050 }
1051
1052 if (result)
1053 {
1055 STATEINFO << "Breaking State '" << getLocalHierarchyString()
1056 << "' (id: " << impl->localUniqueId << ")" << flush;
1057
1058 if (cimpl->__runningTask)
1059 {
1060 cimpl->__runningTask->stop(false);
1061 //runningTask = NULL;
1062 }
1063
1064 IceUtil::Time executionStart = IceUtil::Time::now();
1065
1066 try
1067 {
1069 onBreak();
1070 if (!cimpl->profilersDisabled)
1071 for (const Profiler::ProfilerPtr& localProfiler : cimpl->localProfilers)
1072 {
1073 localProfiler->logEvent(Profiler::Profiler::EventType::eFunctionBreak,
1075 getStateName());
1076 localProfiler->logStatechartLocalParameters(getGlobalHierarchyString(),
1077 localParameters);
1078 localProfiler->logStatechartOutputParameters(getGlobalHierarchyString(),
1079 outputParameters);
1080 }
1082 }
1083 catch (...)
1084 {
1085 ARMARX_ERROR << "onBreak() of State " << globalStateIdentifier << " of class "
1086 << stateClassName << " failed. Ignoring substates and sending Failure.\n"
1088 }
1089
1091
1092 IceUtil::Time duration = IceUtil::Time::now() - executionStart;
1093
1094 if (duration.toMilliSeconds() > MAX_USER_CODE_DURATION)
1095 {
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."
1100 << flush;
1101 }
1102 }
1103
1104 if (result)
1105 {
1106 //reset input parameters, so that they are NOT set when we reenter this state
1107 StateUtilFunctions::unsetParameters(inputParameters);
1109 }
1110
1111 {
1112 std::unique_lock lock(cimpl->__finishedMutex);
1113 cimpl->__finished = true;
1114 cimpl->__finishedCondition.notify_all();
1115 }
1116
1117 return result;
1118}
1119
1120void
1122{
1124 cimpl->localProfilers.insert(profiler);
1125 if (recursiveLevels < 0)
1126 {
1127 // fall through: the value -1 means that all substates should be profiled
1128 }
1129 else if (recursiveLevels == 0)
1130 {
1131 return;
1132 }
1133 else if (recursiveLevels > 0)
1134 {
1135 recursiveLevels--;
1136 }
1137
1138 for (AbstractStateIceBasePtr state : subStateList)
1139 {
1140 StateControllerPtr stateController = StateControllerPtr::dynamicCast(state);
1141
1142 if (stateController)
1143 {
1144 stateController->addProfilerRecursive(profiler, recursiveLevels);
1145 }
1146 }
1147}
1148
1149void
1151{
1153 cimpl->localProfilers.erase(profiler);
1154 if (recursiveLevels < 0)
1155 {
1156 // fall through: the value -1 means that all substates should be profiled
1157 }
1158 else if (recursiveLevels == 0)
1159 {
1160 return;
1161 }
1162 else if (recursiveLevels > 0)
1163 {
1164 recursiveLevels--;
1165 }
1166
1167 for (AbstractStateIceBasePtr state : subStateList)
1168 {
1169 StateControllerPtr stateController = StateControllerPtr::dynamicCast(state);
1170
1171 if (stateController)
1172 {
1173 stateController->addProfilerRecursive(profiler, recursiveLevels);
1174 }
1175 }
1176}
1177
1178bool
1180{
1182 if (!cimpl->__runningTask)
1183 {
1184 return true;
1185 }
1186
1187 return cimpl->__runningTask->isStopped();
1188}
1189
1190bool
1192{
1193 if (!cimpl->__runningTask)
1194 {
1195 return true;
1196 }
1197 return cimpl->__runningTask->isFinished();
1198}
1199
1200void
1202{
1204 if (cimpl->__runningTask)
1205 {
1206 cimpl->__runningTask->join();
1207 }
1208}
1209
1210void
1212{
1213 cimpl->profilersDisabled = disable;
1214}
1215
1216void
1219{
1220 ARMARX_INFO << "Regstering transition code for " << t.evt->eventName;
1221 cimpl->transitionFunctions[getTransitionID(t)] = function;
1222}
1223
1224std::string
1225StateController::getTransitionID(const TransitionIceBase& t) const
1226{
1228 return getTransitionID(
1229 t.evt->eventName,
1230 (t.sourceState ? StateBasePtr::dynamicCast(t.sourceState)->stateName : std::string("")));
1231}
1232
1233std::string
1234StateController::getTransitionID(const std::string& eventName,
1235 const std::string sourceStateName) const
1236{
1237 return sourceStateName + ":" + eventName;
1238}
1239
1240bool
1241StateController::findTransition(const std::string& eventName,
1242 const std::string sourceStateName,
1243 TransitionIceBase& transition)
1244{
1246 for (const TransitionIceBase& t : transitions)
1247 {
1248 if (t.evt->eventName == eventName &&
1249 StateBasePtr::dynamicCast(t.sourceState)->stateName == sourceStateName)
1250 {
1251 transition = t;
1252 return true;
1253 }
1254 }
1255 ARMARX_INFO << "Could not find transition in " << transitions.size() << " transitions";
1256 return false;
1257}
1258
1259bool
1261{
1263 // commented due to performance issues
1264 // const StateBase * curParentState = this;
1265 // while(curParentState)
1266 // {
1267 // if(curParentState->__getUnbreakableBufferSize() > 0){
1268 // return false;
1269 // }
1270 // curParentState = curParentState->__parentState;
1271 // }
1272 // return true;
1273 return !cimpl->__eventBufferedDueToUnbreakableState;
1274}
1275
1276void
1278{
1280 for (unsigned int i = 0; i < subStateList.size(); i++)
1281 {
1282 StateControllerPtr::dynamicCast(subStateList[i])->disableRunFunction();
1283 }
1284
1285 HiddenTimedMutex::ScopedLock lock(impl->__stateMutex);
1286 impl->__useRunFunction = false;
1287
1288 if (cimpl->__runningTask && cimpl->__runningTask->isRunning())
1289 {
1290 ARMARX_VERBOSE << "State with name '" << stateName << "' is waiting for the RunFunction";
1291 cimpl->__runningTask->stop();
1292 }
1293
1294 cimpl->__runningTask = nullptr;
1295}
#define EVENTTOALL
Definition StateBase.h:42
#define STATEINFO
Definition StateBase.h:43
#define MAX_USER_CODE_DURATION
Base Class for all Logging classes.
Definition Logging.h:240
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
Definition StateBase.h:289
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 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.
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.
Definition Logging.h:181
#define ARMARX_ERROR_S
The logging level for unexpected behaviour, that must be fixed.
Definition Logging.h:216
#define ARMARX_ERROR
The logging level for unexpected behaviour, that must be fixed.
Definition Logging.h:196
#define ARMARX_DEBUG
The logging level for output that is only interesting while debugging.
Definition Logging.h:184
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:193
#define ARMARX_VERBOSE
The logging level for verbose information.
Definition Logging.h:187
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 &paramMap, std::string *logOutput=nullptr)
void unsetParameters(StateParameterMap &paramMap)
Sets all entries of the given dictionary to the stored default values.
StringVariantContainerBaseMap getSetValues(const StateParameterMap &paramMap)
This file offers overloads of toIce() and fromIce() functions for STL container types.
IceInternal::Handle< RemoteState > RemoteStatePtr
Definition RemoteState.h:47
@ eTransitionErrorUnexpectedEvent
@ eTransitionErrorUndefined
IceUtil::Handle< ArmarXObjectScheduler > ArmarXObjectSchedulerPtr
Definition ArmarXFwd.h:33
std::string GetHandledExceptionString()
ParameterMappingPtr PMPtr
const LogSender::manipulator flush
Definition LogSender.h:251
IceInternal::Handle< Event > EventPtr
Typedef of EventPtr as IceInternal::Handle<Event> for convenience.
Definition Event.h:40
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
#define ARMARX_TRACE
Definition trace.h:77