SMACC2
Loading...
Searching...
No Matches
smacc_state_machine_impl.hpp
Go to the documentation of this file.
1// Copyright 2021 RobosoftAI Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15/**************************************************************************************************
16 *
17 * Authors: Pablo Inigo Blasco, Brett Aldrich
18 *
19 **************************************************************************************************/
20
21#pragma once
22
23#include <memory>
24#include <sstream>
25#include <string>
26
35
43
44#include <boost/function_types/function_arity.hpp>
45#include <boost/function_types/function_type.hpp>
46#include <boost/function_types/parameter_types.hpp>
48#include <smacc2_msgs/msg/smacc_status.hpp>
49
50namespace smacc2
51{
52using namespace smacc2::introspection;
53
54template <typename TOrthogonal>
56{
57 std::lock_guard<std::recursive_mutex> lock(m_mutex_);
58
61
62 auto it = orthogonals_.find(orthogonalkey);
63
64 if (it != orthogonals_.end())
65 {
67 getLogger(),
68 "Orthogonal %s resource is being required from some state, client or component. Found "
69 "resource in "
70 "cache.",
71 orthogonalkey.c_str());
72 ret = dynamic_cast<TOrthogonal *>(it->second.get());
73 return ret;
74 }
75 else
76 {
77 std::stringstream ss;
78 ss << "Orthogonal not found " << orthogonalkey.c_str() << std::endl;
79 ss << "The existing orthogonals are the following: " << std::endl;
80 for (auto & orthogonal : orthogonals_)
81 {
82 ss << " - " << orthogonal.first << std::endl;
83 }
84
86
87 return nullptr;
88 }
89}
90
91//-------------------------------------------------------------------------------------------------------
92template <typename TOrthogonal>
94{
95 //this->lockStateMachine("create orthogonal");
96 std::lock_guard<std::recursive_mutex> guard(m_mutex_);
97
99
100 if (orthogonals_.count(orthogonalkey) == 0)
101 {
102 auto ret = std::make_shared<TOrthogonal>();
104
105 ret->setStateMachine(this);
106
107 RCLCPP_INFO(getLogger(), "%s Orthogonal is created", orthogonalkey.c_str());
108 }
109 else
110 {
112 getLogger(), "There were already one existing orthogonal of type "
113 << orthogonalkey.c_str() << ". Skipping creation orthogonal request. ");
114 std::stringstream ss;
115 ss << "The existing orthogonals are the following: " << std::endl;
116 for (auto & orthogonal : orthogonals_)
117 {
118 ss << " - " << orthogonal.first << std::endl;
119 }
121 }
122 //this->unlockStateMachine("create orthogonal");
123}
124
125//-------------------------------------------------------------------------------------------------------
126template <typename SmaccComponentType>
128{
130 getLogger(), "component %s is required",
131 demangleSymbol(typeid(SmaccComponentType).name()).c_str());
132 std::lock_guard<std::recursive_mutex> lock(m_mutex_);
133
134 for (auto ortho : this->orthogonals_)
135 {
136 for (auto & client : ortho.second->clients_)
137 {
138 storage = client->getComponent<SmaccComponentType>();
139 if (storage != nullptr)
140 {
141 return;
142 }
143 }
144 }
145
147 getLogger(), "component %s is required but it was not found in any orthogonal",
148 demangleSymbol(typeid(SmaccComponentType).name()).c_str());
149
150 if (throwsException)
151 throw std::runtime_error("component is required but it was not found in any orthogonal");
152
153 // std::string componentkey = demangledTypeName<SmaccComponentType>();
154 // SmaccComponentType *ret;
155
156 // auto it = components_.find(componentkey);
157
158 // if (it == components_.end())
159 // {
160 // RCLCPP_DEBUG(getLogger(),"%s smacc component is required. Creating a new instance.",
161 // componentkey.c_str());
162
163 // ret = new SmaccComponentType();
164 // ret->setStateMachine(this);
165 // components_[componentkey] = static_cast<smacc2::ISmaccComponent *>(ret);
166 // RCLCPP_DEBUG(getLogger(),"%s resource is required. Done.", componentkey.c_str());
167 // }
168 // else
169 // {
170 // RCLCPP_DEBUG(getLogger(),"%s resource is required. Found resource in cache.",
171 // componentkey.c_str()); ret = dynamic_cast<SmaccComponentType *>(it->second);
172 // }
173
174 // storage = ret;
175}
176//-------------------------------------------------------------------------------------------------------
177template <typename EventType>
179{
180 std::lock_guard<std::recursive_mutex> guard(eventQueueMutex_);
181
182#define eventtypename demangleSymbol<EventType>().c_str()
183
185
186 if (
190 {
192 getLogger(),
193 "[ISmaccStateMachine] CURRENT STATE SCOPED EVENT DISCARDED, state is exiting/transitioning "
194 << eventtypename);
195 return;
196 // in this case we may lose/skip events, if this is not right for some cases we should create a
197 // queue to lock the events during the transitions. This issues appeared when a client
198 // asyncbehavior was posting an event meanwhile we were doing the transition, but the main
199 // thread was waiting for its correct finalization (with thread.join)
200 }
201
202 // when a postting event is requested by any component, client, or client behavior
203 // we reach this place. Now, we propagate the events to all the state state reactors to generate
204 // some more events
205
206 RCLCPP_DEBUG_STREAM(getLogger(), "[PostEvent entry point] " << eventtypename);
207 for (auto currentState : currentState_)
208 {
209 propagateEventToStateReactors(currentState, ev);
210 }
211
212 this->signalDetector_->postEvent(ev);
213}
214
215template <typename EventType>
217{
218 auto evname = smacc2::introspection::demangleSymbol<EventType>();
219 RCLCPP_INFO_STREAM(getLogger(), "Event " << evname);
220 auto * ev = new EventType();
221 this->postEvent(ev, evlifetime);
222}
223
224template <typename T>
226{
227 std::lock_guard<std::recursive_mutex> lock(m_mutex_);
228 // RCLCPP_WARN(getLogger(),"get SM Data lock acquire");
229 bool success = false;
230
231 if (!globalData_.count(name))
232 {
233 // RCLCPP_WARN(getLogger(),"get SM Data - data do not exist");
234 success = false;
235 }
236 else
237 {
238 // RCLCPP_WARN(getLogger(),"get SM DAta -data exist. accessing");
239 try
240 {
241 auto & v = globalData_[name];
242
243 // RCLCPP_WARN(getLogger(),"get SM DAta -data exist. any cast");
244 ret = boost::any_cast<T>(v.second);
245 success = true;
246 // RCLCPP_WARN(getLogger(),"get SM DAta -data exist. success");
247 }
248 catch (boost::bad_any_cast & ex)
249 {
250 RCLCPP_ERROR(getLogger(), "bad any cast: %s", ex.what());
251 success = false;
252 }
253 }
254
255 // RCLCPP_WARN(getLogger(),"get SM Data lock release");
256 return success;
257}
258
259template <typename T>
260void ISmaccStateMachine::setGlobalSMData(std::string name, T value)
261{
262 {
263 std::lock_guard<std::recursive_mutex> lock(m_mutex_);
264 // RCLCPP_WARN(getLogger(),"set SM Data lock acquire");
265
266 globalData_[name] = {
267 [this, name]()
268 {
269 std::stringstream ss;
270 auto val = any_cast<T>(globalData_[name].second);
271 ss << val;
272 return ss.str();
273 },
274 value};
275 }
276
277 this->updateStatusMessage();
278}
279
280template <typename StateField, typename BehaviorType>
282{
283 std::string stateFieldName = demangleSymbol(typeid(StateField).name());
284 std::string behaviorType = demangleSymbol(typeid(BehaviorType).name());
286 getLogger(), "Mapping state field '%s' to stateReactor '%s'", stateFieldName.c_str(),
287 behaviorType.c_str());
289 if (!this->getGlobalSMData(stateFieldName, globalreference))
290 {
291 // Using the requires component approach, we force a unique existence
292 // of this component
294 this->requiresComponent(behavior);
296
297 this->setGlobalSMData(stateFieldName, globalreference);
298 }
299}
300
301namespace utils
302{
303template <int arity>
304struct Bind
305{
306 template <typename TSmaccSignal, typename TMemberFunctionPrototype, typename TSmaccObjectType>
307 boost::signals2::connection bindaux(
309 std::shared_ptr<CallbackCounterSemaphore> callbackCounter);
310};
311
312template <>
313struct Bind<1>
314{
315 template <typename TSmaccSignal, typename TMemberFunctionPrototype, typename TSmaccObjectType>
316 boost::signals2::connection bindaux(
318 std::shared_ptr<CallbackCounterSemaphore> callbackCounter)
319 {
320 return signal.connect(
321
322 [=]()
323 {
324 if (callbackCounter == nullptr)
325 {
326 (object->*callback)();
327 }
328 else if (callbackCounter->acquire())
329 {
330 (object->*callback)();
331 callbackCounter->release();
332 }
333 });
334 }
335};
336
337template <>
338struct Bind<2>
339{
340 template <typename TSmaccSignal, typename TMemberFunctionPrototype, typename TSmaccObjectType>
341 boost::signals2::connection bindaux(
343 std::shared_ptr<CallbackCounterSemaphore> callbackCounter)
344 {
345 return signal.connect(
346 [=](auto a1)
347 {
348 if (callbackCounter == nullptr)
349 {
350 return (object->*callback)(a1);
351 }
352 else if (callbackCounter->acquire())
353 {
354 (object->*callback)(a1);
355 callbackCounter->release();
356 }
357 });
358 }
359};
360
361template <>
362struct Bind<3>
363{
364 template <typename TSmaccSignal, typename TMemberFunctionPrototype, typename TSmaccObjectType>
365 boost::signals2::connection bindaux(
367 std::shared_ptr<CallbackCounterSemaphore> callbackCounter)
368 {
369 return signal.connect(
370 [=](auto a1, auto a2)
371 {
372 if (callbackCounter == nullptr)
373 {
374 return (object->*callback)(a1, a2);
375 }
376 else if (callbackCounter->acquire())
377 {
378 (object->*callback)(a1, a2);
379 callbackCounter->release();
380 }
381 });
382 }
383};
384
385template <>
386struct Bind<4>
387{
388 template <typename TSmaccSignal, typename TMemberFunctionPrototype, typename TSmaccObjectType>
389 boost::signals2::connection bindaux(
391 std::shared_ptr<CallbackCounterSemaphore> callbackCounter)
392 {
393 return signal.connect(
394 [=](auto a1, auto a2, auto a3)
395 {
396 if (callbackCounter == nullptr)
397 {
398 return (object->*callback)(a1, a2, a3);
399 }
400 else if (callbackCounter->acquire())
401 {
402 (object->*callback)(a1, a2, a3);
403 callbackCounter->release();
404 }
405 });
406 }
407};
408} // namespace utils
409using namespace smacc2::utils;
410
411template <typename TSmaccSignal, typename TMemberFunctionPrototype, typename TSmaccObjectType>
414{
415 std::lock_guard<std::recursive_mutex> lock(m_mutex_);
416
417 static_assert(
418 std::is_base_of<ISmaccState, TSmaccObjectType>::value ||
419 std::is_base_of<ISmaccClient, TSmaccObjectType>::value ||
420 std::is_base_of<ISmaccClientBehavior, TSmaccObjectType>::value ||
421 std::is_base_of<StateReactor, TSmaccObjectType>::value ||
422 std::is_base_of<ISmaccComponent, TSmaccObjectType>::value,
423 "Only are accepted smacc types as subscribers for smacc signals");
424
425 typedef decltype(callback) ft;
427 boost::signals2::connection connection;
428
429 // long life-time objects
430 if (
431 std::is_base_of<ISmaccComponent, TSmaccObjectType>::value ||
432 std::is_base_of<ISmaccClient, TSmaccObjectType>::value ||
433 std::is_base_of<ISmaccOrthogonal, TSmaccObjectType>::value ||
434 std::is_base_of<ISmaccStateMachine, TSmaccObjectType>::value)
435 {
437 getLogger(),
438 "[StateMachine] long life-time smacc signal subscription created. Subscriber is %s. Callback "
439 "is: %s",
441 demangleSymbol(typeid(callback).name()).c_str());
442
443 connection = binder.bindaux(signal, callback, object, nullptr);
444 }
445 else if (
446 std::is_base_of<ISmaccState, TSmaccObjectType>::value ||
447 std::is_base_of<StateReactor, TSmaccObjectType>::value ||
448 std::is_base_of<ISmaccClientBehavior, TSmaccObjectType>::value)
449 {
451 getLogger(),
452 "[StateMachine] life-time constrained smacc signal subscription created. Subscriber is %s",
454
455 std::shared_ptr<CallbackCounterSemaphore> callbackCounterSemaphore;
456 if (stateCallbackConnections.count(object))
457 {
459 }
460 else
461 {
463 std::make_shared<CallbackCounterSemaphore>(demangledTypeName<TSmaccObjectType>().c_str());
465 }
466
467 connection = binder.bindaux(signal, callback, object, callbackCounterSemaphore);
468 callbackCounterSemaphore->addConnection(connection);
469 }
470 else // state life-time objects
471 {
473 getLogger(),
474 "[StateMachine] connecting signal to an unknown object with life-time unknown "
475 "behavior. It might provoke "
476 "an exception if the object is destroyed during the execution.");
477
478 connection = binder.bindaux(signal, callback, object, nullptr);
479 }
480
481 return connection;
482}
483
484template <typename StateType>
486{
487 std::lock_guard<std::recursive_mutex> lock(m_mutex_);
488
490 getLogger(),
491 "[State Machne] Initializating a new state '%s' and updating current state. Getting state "
492 "meta-information. number of orthogonals: %ld",
493 demangleSymbol(typeid(StateType).name()).c_str(), this->orthogonals_.size());
494
496 currentState_.push_back(state);
498}
499
500template <typename StateType>
502{
504 getLogger(), "[%s] State OnEntry code finished",
505 demangleSymbol(typeid(StateType).name()).c_str());
506
507 auto currentState = this->currentState_.back();
508 for (auto pair : this->orthogonals_)
509 {
510 RCLCPP_DEBUG(getLogger(), "ortho onentry: %s", pair.second->getName().c_str());
511 auto & orthogonal = pair.second;
512 try
513 {
514 orthogonal->onEntry();
515 }
516 catch (const std::exception & e)
517 {
519 getLogger(),
520 "[Orthogonal %s] Exception on Entry - continuing with next orthogonal. Exception info: %s",
521 pair.second->getName().c_str(), e.what());
522 }
523 }
524
525 for (auto & sr : currentState->getStateReactors())
526 {
527 auto srname = smacc2::demangleSymbol(typeid(*sr).name());
528 RCLCPP_INFO_STREAM(getLogger(), "state reactor onEntry: " << srname);
529 try
530 {
531 sr->onEntry();
532 }
533 catch (const std::exception & e)
534 {
536 getLogger(),
537 "[State Reactor %s] Exception on Entry - continuing with next state reactor. Exception "
538 "info: %s",
539 srname.c_str(), e.what());
540 }
541 }
542
543 for (auto & eg : currentState->getEventGenerators())
544 {
545 auto egname = smacc2::demangleSymbol(typeid(*eg).name());
546 RCLCPP_INFO_STREAM(getLogger(), "event generator onEntry: " << egname);
547 try
548 {
549 eg->onEntry();
550 }
551 catch (const std::exception & e)
552 {
554 getLogger(),
555 "[Event generator %s] Exception on Entry - continuing with next state reactor. Exception "
556 "info: %s",
557 egname.c_str(), e.what());
558 }
559 }
560
561 {
562 std::lock_guard<std::recursive_mutex> lock(m_mutex_);
563 this->updateStatusMessage();
565 }
566}
567
568template <typename StateType>
570{
571 for (auto pair : this->orthogonals_)
572 {
573 // RCLCPP_INFO(getLogger(),"ortho onruntime configure: %s", pair.second->getName().c_str());
574 auto & orthogonal = pair.second;
575 orthogonal->runtimeConfigure();
576 }
577
578 {
579 std::lock_guard<std::recursive_mutex> lock(m_mutex_);
580 this->updateStatusMessage();
582
584 }
585}
586
587template <typename StateType>
592
593template <typename StateType>
595{
597 auto fullname = demangleSymbol(typeid(StateType).name());
598 RCLCPP_WARN_STREAM(getLogger(), "exiting state: " << fullname);
599 // this->set_parameter("destroyed", true);
600
601 RCLCPP_INFO_STREAM(getLogger(), "Notification State Exit: leaving state " << state);
602 for (auto pair : this->orthogonals_)
603 {
604 auto & orthogonal = pair.second;
605 try
606 {
607 orthogonal->onExit();
608 }
609 catch (const std::exception & e)
610 {
612 getLogger(),
613 "[Orthogonal %s] Exception onExit - continuing with next orthogonal. Exception info: %s",
614 pair.second->getName().c_str(), e.what());
615 }
616 }
617
618 for (auto & sr : state->getStateReactors())
619 {
620 auto srname = smacc2::demangleSymbol(typeid(*sr).name());
621 RCLCPP_INFO_STREAM(getLogger(), "state reactor OnExit: " << srname);
622 try
623 {
624 sr->onExit();
625 }
626 catch (const std::exception & e)
627 {
629 getLogger(),
630 "[State Reactor %s] Exception on OnExit - continuing with next state reactor. Exception "
631 "info: %s",
632 srname.c_str(), e.what());
633 }
634 }
635
636 for (auto & eg : state->getEventGenerators())
637 {
638 auto egname = smacc2::demangleSymbol(typeid(*eg).name());
639 RCLCPP_INFO_STREAM(getLogger(), "event generator OnExit: " << egname);
640 try
641 {
642 eg->onExit();
643 }
644 catch (const std::exception & e)
645 {
647 getLogger(),
648 "[State Reactor %s] Exception on OnExit - continuing with next state reactor. Exception "
649 "info: %s",
650 egname.c_str(), e.what());
651 }
652 }
653}
654
655template <typename StateType>
657{
658 this->lockStateMachine("state exit");
659
661
662 auto fullname = demangleSymbol(typeid(StateType).name());
663 RCLCPP_WARN_STREAM(getLogger(), "exiting state: " << fullname);
664
665 RCLCPP_INFO_STREAM(getLogger(), "Notification State Disposing: leaving state" << state);
666 for (auto pair : this->orthogonals_)
667 {
668 auto & orthogonal = pair.second;
669 try
670 {
671 orthogonal->onDispose();
672 }
673 catch (const std::exception & e)
674 {
676 getLogger(),
677 "[Orthogonal %s] Exception onDispose - continuing with next orthogonal. Exception info: %s",
678 pair.second->getName().c_str(), e.what());
679 }
680 }
681
682 for (auto & sr : state->getStateReactors())
683 {
684 auto srname = smacc2::demangleSymbol(typeid(*sr).name()).c_str();
685 RCLCPP_INFO(getLogger(), "state reactor disposing: %s", srname);
686 try
687 {
688 this->disconnectSmaccSignalObject(sr.get());
689 }
690 catch (const std::exception & e)
691 {
693 getLogger(),
694 "[State Reactor %s] Exception on OnDispose - continuing with next state reactor. Exception "
695 "info: %s",
696 srname, e.what());
697 }
698 }
699
700 for (auto & eg : state->getEventGenerators())
701 {
702 auto egname = smacc2::demangleSymbol(typeid(*eg).name()).c_str();
703 RCLCPP_INFO(getLogger(), "state reactor disposing: %s", egname);
704 try
705 {
706 this->disconnectSmaccSignalObject(eg.get());
707 }
708 catch (const std::exception & e)
709 {
711 getLogger(),
712 "[State Reactor %s] Exception on OnDispose - continuing with next state reactor. Exception "
713 "info: %s",
714 egname, e.what());
715 }
716 }
717
718 this->stateCallbackConnections.clear();
719 currentState_.pop_back();
720
721 // then call exit state
722 RCLCPP_WARN_STREAM(getLogger(), "state exit: " << fullname);
723
725 this->unlockStateMachine("state exit");
726}
727//------------------------------------------------------------------------------------------------
728template <typename EventType>
730{
732 getLogger(), "PROPAGATING EVENT [%s] TO SRs [%s]: ", demangleSymbol<EventType>().c_str(),
733 st->getClassName().c_str());
734 for (auto & sb : st->getStateReactors())
735 {
736 sb->notifyEvent(ev);
737 }
738
739 auto * pst = st->getParentState();
740 if (pst != nullptr)
741 {
743 }
744}
745
746template <typename InitialStateType>
748{
749 this->stateMachineInfo_ = std::make_shared<SmaccStateMachineInfo>(this->getNode());
750 this->stateMachineInfo_->buildStateMachineInfo<InitialStateType>();
751 this->stateMachineInfo_->assembleSMStructureMessage(this);
753}
754
756
758
763
764} // namespace smacc2
std::map< std::string, std::shared_ptr< smacc2::ISmaccOrthogonal > > orthogonals_
std::vector< ISmaccState * > currentState_
std::recursive_mutex eventQueueMutex_
bool getGlobalSMData(std::string name, T &ret)
boost::signals2::connection createSignalConnection(TSmaccSignal &signal, TMemberFunctionPrototype callback, TSmaccObjectType *object)
StateMachineInternalAction stateMachineCurrentAction
std::shared_ptr< SmaccStateInfo > currentStateInfo_
rclcpp::Node::SharedPtr getNode()
std::map< std::string, std::pair< std::function< std::string()>, boost::any > > globalData_
void notifyOnStateExitting(StateType *state)
void setGlobalSMData(std::string name, T value)
void notifyOnRuntimeConfigurationFinished(StateType *state)
void notifyOnStateExited(StateType *state)
void lockStateMachine(std::string msg)
void notifyOnStateEntryEnd(StateType *state)
void propagateEventToStateReactors(ISmaccState *st, EventType *ev)
void disconnectSmaccSignalObject(void *object)
const SmaccStateMachineInfo & getStateMachineInfo()
void unlockStateMachine(std::string msg)
void requiresComponent(SmaccComponentType *&storage, bool throwsExceptionIfNotExist=false)
std::map< void *, std::shared_ptr< CallbackCounterSemaphore > > stateCallbackConnections
void notifyOnRuntimeConfigured(StateType *state)
std::shared_ptr< SmaccStateMachineInfo > stateMachineInfo_
void postEvent(EventType *ev, EventLifeTime evlifetime=EventLifeTime::ABSOLUTE)
void notifyOnStateEntryStart(StateType *state)
void notifyStateExited(ISmaccState *currentState)
void notifyStateConfigured(ISmaccState *currentState)
std::string demangleSymbol()
#define eventtypename
void TRACEPOINT(spinOnce)
boost::signals2::connection bindaux(TSmaccSignal &signal, TMemberFunctionPrototype callback, TSmaccObjectType *object, std::shared_ptr< CallbackCounterSemaphore > callbackCounter)
boost::signals2::connection bindaux(TSmaccSignal &signal, TMemberFunctionPrototype callback, TSmaccObjectType *object, std::shared_ptr< CallbackCounterSemaphore > callbackCounter)
boost::signals2::connection bindaux(TSmaccSignal &signal, TMemberFunctionPrototype callback, TSmaccObjectType *object, std::shared_ptr< CallbackCounterSemaphore > callbackCounter)
boost::signals2::connection bindaux(TSmaccSignal &signal, TMemberFunctionPrototype callback, TSmaccObjectType *object, std::shared_ptr< CallbackCounterSemaphore > callbackCounter)
boost::signals2::connection bindaux(TSmaccSignal &signal, TMemberFunctionPrototype callback, TSmaccObjectType *object, std::shared_ptr< CallbackCounterSemaphore > callbackCounter)
smacc2_event