SMACC2
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
59 std::string orthogonalkey = demangledTypeName<TOrthogonal>();
60 TOrthogonal * ret;
61
62 auto it = orthogonals_.find(orthogonalkey);
63
64 if (it != orthogonals_.end())
65 {
66 RCLCPP_DEBUG(
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
85 RCLCPP_WARN_STREAM(getLogger(), ss.str());
86
87 return nullptr;
88 }
89}
90
91//-------------------------------------------------------------------------------------------------------
92template <typename TOrthogonal>
94{
95 //this->lockStateMachine("create orthogonal");
96 std::lock_guard<std::recursive_mutex> lock(m_mutex_);
97 std::string orthogonalkey = demangledTypeName<TOrthogonal>();
98
99 if (orthogonals_.count(orthogonalkey) == 0)
100 {
101 auto ret = std::make_shared<TOrthogonal>();
102 orthogonals_[orthogonalkey] = dynamic_pointer_cast<smacc2::ISmaccOrthogonal>(ret);
103
104 ret->setStateMachine(this);
105
106 RCLCPP_INFO(getLogger(), "%s Orthogonal is created", orthogonalkey.c_str());
107 }
108 else
109 {
110 RCLCPP_WARN_STREAM(
111 getLogger(), "There were already one existing orthogonal of type "
112 << orthogonalkey.c_str() << ". Skipping creation orthogonal request. ");
113 std::stringstream ss;
114 ss << "The existing orthogonals are the following: " << std::endl;
115 for (auto & orthogonal : orthogonals_)
116 {
117 ss << " - " << orthogonal.first << std::endl;
118 }
119 RCLCPP_WARN_STREAM(getLogger(), ss.str());
120 }
121 //this->unlockStateMachine("create orthogonal");
122}
123
124//-------------------------------------------------------------------------------------------------------
125template <typename SmaccComponentType>
126void ISmaccStateMachine::requiresComponent(SmaccComponentType *& storage)
127{
128 RCLCPP_DEBUG(
129 getLogger(), "component %s is required",
130 demangleSymbol(typeid(SmaccComponentType).name()).c_str());
131 std::lock_guard<std::recursive_mutex> lock(m_mutex_);
132
133 for (auto ortho : this->orthogonals_)
134 {
135 for (auto & client : ortho.second->clients_)
136 {
137 storage = client->getComponent<SmaccComponentType>();
138 if (storage != nullptr)
139 {
140 return;
141 }
142 }
143 }
144
145 RCLCPP_WARN(
146 getLogger(), "component %s is required but it was not found in any orthogonal",
147 demangleSymbol(typeid(SmaccComponentType).name()).c_str());
148
149 // std::string componentkey = demangledTypeName<SmaccComponentType>();
150 // SmaccComponentType *ret;
151
152 // auto it = components_.find(componentkey);
153
154 // if (it == components_.end())
155 // {
156 // RCLCPP_DEBUG(getLogger(),"%s smacc component is required. Creating a new instance.",
157 // componentkey.c_str());
158
159 // ret = new SmaccComponentType();
160 // ret->setStateMachine(this);
161 // components_[componentkey] = static_cast<smacc2::ISmaccComponent *>(ret);
162 // RCLCPP_DEBUG(getLogger(),"%s resource is required. Done.", componentkey.c_str());
163 // }
164 // else
165 // {
166 // RCLCPP_DEBUG(getLogger(),"%s resource is required. Found resource in cache.",
167 // componentkey.c_str()); ret = dynamic_cast<SmaccComponentType *>(it->second);
168 // }
169
170 // storage = ret;
171}
172//-------------------------------------------------------------------------------------------------------
173template <typename EventType>
174void ISmaccStateMachine::postEvent(EventType * ev, EventLifeTime evlifetime)
175{
176 std::lock_guard<std::recursive_mutex> guard(eventQueueMutex_);
177
178#define eventtypename demangleSymbol<EventType>().c_str()
179
181
182 if (
183 evlifetime == EventLifeTime::CURRENT_STATE &&
186 {
187 RCLCPP_WARN_STREAM(
188 getLogger(),
189 "[ISmaccStateMachine] CURRENT STATE SCOPED EVENT DISCARDED, state is exiting/transitioning "
190 << eventtypename);
191 return;
192 // in this case we may lose/skip events, if this is not right for some cases we should create a
193 // queue to lock the events during the transitions. This issues appeared when a client
194 // asyncbehavior was posting an event meanwhile we were doing the transition, but the main
195 // thread was waiting for its correct finalization (with thread.join)
196 }
197
198 // when a postting event is requested by any component, client, or client behavior
199 // we reach this place. Now, we propagate the events to all the state state reactors to generate
200 // some more events
201
202 RCLCPP_DEBUG_STREAM(getLogger(), "[PostEvent entry point] " << eventtypename);
203 auto currentstate = currentState_;
204 if (currentstate != nullptr)
205 {
206 propagateEventToStateReactors(currentstate, ev);
207 }
208
209 this->signalDetector_->postEvent(ev);
210}
211
212template <typename EventType>
214{
215 auto evname = smacc2::introspection::demangleSymbol<EventType>();
216 RCLCPP_INFO_STREAM(getLogger(), "Event " << evname);
217 auto * ev = new EventType();
218 this->postEvent(ev, evlifetime);
219}
220
221template <typename T>
222bool ISmaccStateMachine::getGlobalSMData(std::string name, T & ret)
223{
224 std::lock_guard<std::recursive_mutex> lock(m_mutex_);
225 // RCLCPP_WARN(getLogger(),"get SM Data lock acquire");
226 bool success = false;
227
228 if (!globalData_.count(name))
229 {
230 // RCLCPP_WARN(getLogger(),"get SM Data - data do not exist");
231 success = false;
232 }
233 else
234 {
235 // RCLCPP_WARN(getLogger(),"get SM DAta -data exist. accessing");
236 try
237 {
238 auto & v = globalData_[name];
239
240 // RCLCPP_WARN(getLogger(),"get SM DAta -data exist. any cast");
241 ret = boost::any_cast<T>(v.second);
242 success = true;
243 // RCLCPP_WARN(getLogger(),"get SM DAta -data exist. success");
244 }
245 catch (boost::bad_any_cast & ex)
246 {
247 RCLCPP_ERROR(getLogger(), "bad any cast: %s", ex.what());
248 success = false;
249 }
250 }
251
252 // RCLCPP_WARN(getLogger(),"get SM Data lock release");
253 return success;
254}
255
256template <typename T>
257void ISmaccStateMachine::setGlobalSMData(std::string name, T value)
258{
259 {
260 std::lock_guard<std::recursive_mutex> lock(m_mutex_);
261 // RCLCPP_WARN(getLogger(),"set SM Data lock acquire");
262
263 globalData_[name] = {
264 [this, name]() {
265 std::stringstream ss;
266 auto val = any_cast<T>(globalData_[name].second);
267 ss << val;
268 return ss.str();
269 },
270 value};
271 }
272
273 this->updateStatusMessage();
274}
275
276template <typename StateField, typename BehaviorType>
278{
279 std::string stateFieldName = demangleSymbol(typeid(StateField).name());
280 std::string behaviorType = demangleSymbol(typeid(BehaviorType).name());
281 RCLCPP_INFO(
282 getLogger(), "Mapping state field '%s' to stateReactor '%s'", stateFieldName.c_str(),
283 behaviorType.c_str());
284 SmaccClientBehavior * globalreference;
285 if (!this->getGlobalSMData(stateFieldName, globalreference))
286 {
287 // Using the requires component approach, we force a unique existence
288 // of this component
289 BehaviorType * behavior;
290 this->requiresComponent(behavior);
291 globalreference = dynamic_cast<ISmaccClientBehavior *>(behavior);
292
293 this->setGlobalSMData(stateFieldName, globalreference);
294 }
295}
296
297namespace utils
298{
299template <int arity>
300struct Bind
301{
302 template <typename TSmaccSignal, typename TMemberFunctionPrototype, typename TSmaccObjectType>
303 boost::signals2::connection bindaux(
304 TSmaccSignal & signal, TMemberFunctionPrototype callback, TSmaccObjectType * object);
305};
306
307template <>
308struct Bind<1>
309{
310 template <typename TSmaccSignal, typename TMemberFunctionPrototype, typename TSmaccObjectType>
311 boost::signals2::connection bindaux(
312 TSmaccSignal & signal, TMemberFunctionPrototype callback, TSmaccObjectType * object)
313 {
314 return signal.connect([=]() { return (object->*callback)(); });
315 }
316};
317
318template <>
319struct Bind<2>
320{
321 template <typename TSmaccSignal, typename TMemberFunctionPrototype, typename TSmaccObjectType>
322 boost::signals2::connection bindaux(
323 TSmaccSignal & signal, TMemberFunctionPrototype callback, TSmaccObjectType * object)
324 {
325 return signal.connect([=](auto a1) { return (object->*callback)(a1); });
326 }
327};
328
329template <>
330struct Bind<3>
331{
332 template <typename TSmaccSignal, typename TMemberFunctionPrototype, typename TSmaccObjectType>
333 boost::signals2::connection bindaux(
334 TSmaccSignal & signal, TMemberFunctionPrototype callback, TSmaccObjectType * object)
335 {
336 return signal.connect([=](auto a1, auto a2) { return (object->*callback)(a1, a2); });
337 }
338};
339
340template <>
341struct Bind<4>
342{
343 template <typename TSmaccSignal, typename TMemberFunctionPrototype, typename TSmaccObjectType>
344 boost::signals2::connection bindaux(
345 TSmaccSignal & signal, TMemberFunctionPrototype callback, TSmaccObjectType * object)
346 {
347 return signal.connect(
348 [=](auto a1, auto a2, auto a3) { return (object->*callback)(a1, a2, a3); });
349 }
350};
351} // namespace utils
352using namespace smacc2::utils;
353
354template <typename TSmaccSignal, typename TMemberFunctionPrototype, typename TSmaccObjectType>
356 TSmaccSignal & signal, TMemberFunctionPrototype callback, TSmaccObjectType * object)
357{
358 static_assert(
359 std::is_base_of<ISmaccState, TSmaccObjectType>::value ||
360 std::is_base_of<ISmaccClient, TSmaccObjectType>::value ||
361 std::is_base_of<ISmaccClientBehavior, TSmaccObjectType>::value ||
362 std::is_base_of<StateReactor, TSmaccObjectType>::value ||
363 std::is_base_of<ISmaccComponent, TSmaccObjectType>::value,
364 "Only are accepted smacc types as subscribers for smacc signals");
365
366 typedef decltype(callback) ft;
368 boost::signals2::connection connection = binder.bindaux(signal, callback, object);
369
370 // long life-time objects
371 if (
372 std::is_base_of<ISmaccComponent, TSmaccObjectType>::value ||
373 std::is_base_of<ISmaccClient, TSmaccObjectType>::value ||
374 std::is_base_of<ISmaccOrthogonal, TSmaccObjectType>::value ||
375 std::is_base_of<ISmaccStateMachine, TSmaccObjectType>::value)
376 {
377 }
378 else if (
379 std::is_base_of<ISmaccState, TSmaccObjectType>::value ||
380 std::is_base_of<StateReactor, TSmaccObjectType>::value ||
381 std::is_base_of<ISmaccClientBehavior, TSmaccObjectType>::value)
382 {
383 RCLCPP_INFO(
384 getLogger(),
385 "[StateMachine] life-time constrained smacc signal subscription created. Subscriber is %s",
386 demangledTypeName<TSmaccObjectType>().c_str());
387 stateCallbackConnections.push_back(connection);
388 }
389 else // state life-time objects
390 {
391 RCLCPP_WARN(
392 getLogger(),
393 "[StateMachine] connecting signal to an unknown object with life-time unknown "
394 "behavior. It might provoke "
395 "an exception if the object is destroyed during the execution.");
396 }
397
398 return connection;
399}
400
401template <typename StateType>
403{
404 std::lock_guard<std::recursive_mutex> lock(m_mutex_);
405
406 RCLCPP_DEBUG(
407 getLogger(),
408 "[State Machne] Initializating a new state '%s' and updating current state. Getting state "
409 "meta-information. number of orthogonals: %ld",
410 demangleSymbol(typeid(StateType).name()).c_str(), this->orthogonals_.size());
411
413 currentState_ = state;
414 currentStateInfo_ = stateMachineInfo_->getState<StateType>();
415}
416
417template <typename StateType>
419{
420 RCLCPP_INFO(
421 getLogger(), "[%s] State OnEntry code finished",
422 demangleSymbol(typeid(StateType).name()).c_str());
423
424 for (auto pair : this->orthogonals_)
425 {
426 RCLCPP_DEBUG(getLogger(), "ortho onentry: %s", pair.second->getName().c_str());
427 auto & orthogonal = pair.second;
428 try
429 {
430 orthogonal->onEntry();
431 }
432 catch (const std::exception & e)
433 {
434 RCLCPP_ERROR(
435 getLogger(),
436 "[Orthogonal %s] Exception on Entry - continuing with next orthogonal. Exception info: %s",
437 pair.second->getName().c_str(), e.what());
438 }
439 }
440
441 for (auto & sr : this->currentState_->getStateReactors())
442 {
443 auto srname = smacc2::demangleSymbol(typeid(*sr).name());
444 RCLCPP_INFO_STREAM(getLogger(), "state reactor onEntry: " << srname);
445 try
446 {
447 sr->onEntry();
448 }
449 catch (const std::exception & e)
450 {
451 RCLCPP_ERROR(
452 getLogger(),
453 "[State Reactor %s] Exception on Entry - continuing with next state reactor. Exception "
454 "info: %s",
455 srname.c_str(), e.what());
456 }
457 }
458
459 for (auto & eg : this->currentState_->getEventGenerators())
460 {
461 auto egname = smacc2::demangleSymbol(typeid(*eg).name());
462 RCLCPP_INFO_STREAM(getLogger(), "event generator onEntry: " << egname);
463 try
464 {
465 eg->onEntry();
466 }
467 catch (const std::exception & e)
468 {
469 RCLCPP_ERROR(
470 getLogger(),
471 "[Event generator %s] Exception on Entry - continuing with next state reactor. Exception "
472 "info: %s",
473 egname.c_str(), e.what());
474 }
475 }
476
477 this->updateStatusMessage();
479}
480
481template <typename StateType>
483{
484 for (auto pair : this->orthogonals_)
485 {
486 // RCLCPP_INFO(getLogger(),"ortho onruntime configure: %s", pair.second->getName().c_str());
487 auto & orthogonal = pair.second;
488 orthogonal->runtimeConfigure();
489 }
490
491 this->updateStatusMessage();
492
494}
495
496template <typename StateType>
498{
500}
501
502template <typename StateType>
504{
506
507 auto fullname = demangleSymbol(typeid(StateType).name());
508 RCLCPP_WARN_STREAM(getLogger(), "exiting state: " << fullname);
509 // this->set_parameter("destroyed", true);
510
511 RCLCPP_INFO_STREAM(getLogger(), "Notification State Exit: leaving state " << state);
512 for (auto pair : this->orthogonals_)
513 {
514 auto & orthogonal = pair.second;
515 try
516 {
517 orthogonal->onExit();
518 }
519 catch (const std::exception & e)
520 {
521 RCLCPP_ERROR(
522 getLogger(),
523 "[Orthogonal %s] Exception onExit - continuing with next orthogonal. Exception info: %s",
524 pair.second->getName().c_str(), e.what());
525 }
526 }
527
528 for (auto & sr : state->getStateReactors())
529 {
530 auto srname = smacc2::demangleSymbol(typeid(*sr).name());
531 RCLCPP_INFO_STREAM(getLogger(), "state reactor OnExit: " << srname);
532 try
533 {
534 sr->onExit();
535 }
536 catch (const std::exception & e)
537 {
538 RCLCPP_ERROR(
539 getLogger(),
540 "[State Reactor %s] Exception on OnExit - continuing with next state reactor. Exception "
541 "info: %s",
542 srname.c_str(), e.what());
543 }
544 }
545
546 for (auto & eg : state->getEventGenerators())
547 {
548 auto egname = smacc2::demangleSymbol(typeid(*eg).name());
549 RCLCPP_INFO_STREAM(getLogger(), "event generator OnExit: " << egname);
550 try
551 {
552 eg->onExit();
553 }
554 catch (const std::exception & e)
555 {
556 RCLCPP_ERROR(
557 getLogger(),
558 "[State Reactor %s] Exception on OnExit - continuing with next state reactor. Exception "
559 "info: %s",
560 egname.c_str(), e.what());
561 }
562 }
563
564 //this->lockStateMachine("state exit");
565}
566
567template <typename StateType>
569{
570 auto fullname = demangleSymbol(typeid(StateType).name());
571
572 // then call exit state
573 RCLCPP_WARN_STREAM(getLogger(), "state exit: " << fullname);
574
576 //this->unlockStateMachine("state exit");
577}
578//------------------------------------------------------------------------------------------------
579template <typename EventType>
581{
582 RCLCPP_DEBUG(
583 getLogger(), "PROPAGATING EVENT [%s] TO SRs [%s]: ", demangleSymbol<EventType>().c_str(),
584 st->getClassName().c_str());
585 for (auto & sb : st->getStateReactors())
586 {
587 sb->notifyEvent(ev);
588 }
589
590 auto * pst = st->getParentState();
591 if (pst != nullptr)
592 {
594 }
595}
596
597template <typename InitialStateType>
599{
600 this->stateMachineInfo_ = std::make_shared<SmaccStateMachineInfo>(this->getNode());
601 this->stateMachineInfo_->buildStateMachineInfo<InitialStateType>();
602 this->stateMachineInfo_->assembleSMStructureMessage(this);
604}
605
607
609
611{
612 return *this->stateMachineInfo_;
613}
614
615} // namespace smacc2
std::map< std::string, std::shared_ptr< smacc2::ISmaccOrthogonal > > orthogonals_
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 notifyOnStateEntryEnd(StateType *state)
void propagateEventToStateReactors(ISmaccState *st, EventType *ev)
const SmaccStateMachineInfo & getStateMachineInfo()
void requiresComponent(SmaccComponentType *&storage)
unsigned long getCurrentStateCounter() const
void notifyOnRuntimeConfigured(StateType *state)
std::list< boost::signals2::connection > stateCallbackConnections
std::shared_ptr< SmaccStateMachineInfo > stateMachineInfo_
void postEvent(EventType *ev, EventLifeTime evlifetime=EventLifeTime::ABSOLUTE)
void notifyOnStateEntryStart(StateType *state)
ISmaccState * getParentState()
Definition: smacc_state.hpp:32
virtual std::string getClassName()
Definition: smacc_state.cpp:26
std::vector< std::shared_ptr< StateReactor > > & getStateReactors()
Definition: smacc_state.hpp:80
std::vector< std::shared_ptr< SmaccEventGenerator > > & getEventGenerators()
Definition: smacc_state.hpp:82
void postEvent(EventType *ev)
std::string demangleSymbol(const std::string &name)
void callback(const image_tools::ROSCvMatContainer &img)
#define eventtypename
void TRACEPOINT(spinOnce)
boost::signals2::connection bindaux(TSmaccSignal &signal, TMemberFunctionPrototype callback, TSmaccObjectType *object)
boost::signals2::connection bindaux(TSmaccSignal &signal, TMemberFunctionPrototype callback, TSmaccObjectType *object)
boost::signals2::connection bindaux(TSmaccSignal &signal, TMemberFunctionPrototype callback, TSmaccObjectType *object)
boost::signals2::connection bindaux(TSmaccSignal &signal, TMemberFunctionPrototype callback, TSmaccObjectType *object)
boost::signals2::connection bindaux(TSmaccSignal &signal, TMemberFunctionPrototype callback, TSmaccObjectType *object)
smacc2_event