SMACC2
Loading...
Searching...
No Matches
smacc_state_base.hpp
Go to the documentation of this file.
1// Copyright 2025 Robosoft 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
28
29#define STATE_NAME (demangleSymbol(typeid(MostDerived).name()).c_str())
30
31namespace smacc2
32{
33using namespace smacc2::introspection;
34using namespace smacc2::default_events;
35
36template <
37 class MostDerived, class Context, class InnerInitial = mpl::list<>,
38 sc::history_mode historyMode = sc::has_deep_history>
39class SmaccState : public sc::simple_state<MostDerived, Context, InnerInitial, historyMode>,
40 public ISmaccState
41{
43
44public:
45 typedef Context TContext;
46 typedef typename Context::inner_context_type context_type;
47 typedef typename context_type::state_iterator state_iterator;
48
49 typedef InnerInitial LastDeepState;
50
52 InnerInitial * smacc_inner_type;
53
56 {
57 my_context(typename base_type::context_ptr_type pContext) : pContext_(pContext) {}
58
59 typename base_type::context_ptr_type pContext_;
60 };
61
62 SmaccState() = delete;
63
64 // Constructor that initializes the state ros node handle
66 {
67 static_assert(
68 std::is_base_of<ISmaccState, Context>::value ||
69 std::is_base_of<ISmaccStateMachine, Context>::value,
70 "The context class must be a SmaccState or a SmaccStateMachine");
71 static_assert(
72 !std::is_same<MostDerived, Context>::value,
73 "The context must be a different state or state machine "
74 "than the current state");
75
76 logger_.reset(new rclcpp::Logger(
77 rclcpp::get_logger(smacc2::utils::cleanShortTypeName(typeid(MostDerived)))));
78
79 RCLCPP_INFO(getLogger(), "[%s] Creating state ", STATE_NAME);
80 this->set_context(ctx.pContext_);
81
82 node_ = this->getStateMachine().getNode();
83
84 this->stateInfo_ = getStateInfo();
85
86 // storing a reference to the parent state
87 auto & ps = this->template context<Context>();
88 parentState_ = dynamic_cast<ISmaccState *>(&ps);
89 finishStateThrown = false;
90 }
91
92 virtual ~SmaccState() {}
93
95 {
96 auto smInfo = this->getStateMachine().getStateMachineInfo();
97
98 auto key = typeid(MostDerived).name();
99 if (smInfo.states.count(key))
100 {
101 return smInfo.states[key].get();
102 }
103 else
104 {
105 return nullptr;
106 }
107 }
108
109 std::string getName() override { return getShortName(); }
110
111 std::string getFullName() { return demangleSymbol(typeid(MostDerived).name()); }
112
113 std::string getShortName() { return smacc2::utils::cleanShortTypeName(typeid(MostDerived)); }
114
116
117 // this function is called by boot statechart before the destructor call
118 void exit()
119 {
120 auto * derivedThis = static_cast<MostDerived *>(this);
121 {
122 std::lock_guard<std::recursive_mutex> lock(this->getStateMachine().getMutex());
123 this->getStateMachine().notifyOnStateExiting(derivedThis);
124 try
125 {
126 TRACETOOLS_TRACEPOINT(smacc2_state_onExit_start, STATE_NAME);
127 standardOnExit(*derivedThis);
128 TRACETOOLS_TRACEPOINT(smacc2_state_onExit_end, STATE_NAME);
129 }
130 catch (...)
131 {
132 }
133 this->getStateMachine().notifyOnStateExited(derivedThis);
134 }
135 }
136
137public:
138 // This method is static-polymorphic because of the curiously recurring template pattern. It
139 // calls to the most derived class onEntry method if declared on smacc state construction
141
142 // This method is static-polymorphic because of the curiously recurring template pattern. It
143 // calls to the most derived class onEntry method if declared on smacc state construction
144 void onEntry() {}
145
146 // this method is static-polimorphic because of the curiously recurreing pattern. It
147 // calls to the most derived class onExit method if declared on smacc state destruction
148 void onExit() {}
149
150 template <typename T>
151 bool getGlobalSMData(std::string name, T & ret)
152 {
153 return base_type::outermost_context().getGlobalSMData(name, ret);
154 }
155
156 // Store globally in this state machine. (By value parameter )
157 template <typename T>
158 void setGlobalSMData(std::string name, T value)
159 {
160 base_type::outermost_context().setGlobalSMData(name, value);
161 }
162
163 template <typename SmaccComponentType>
164 void requiresComponent(SmaccComponentType *& storage)
165 {
166 base_type::outermost_context().requiresComponent(storage);
167 }
168
169 virtual ISmaccStateMachine & getStateMachine() { return base_type::outermost_context(); }
170
171 template <typename TOrthogonal, typename TBehavior>
173 std::function<void(TBehavior & bh, MostDerived &)> initializationFunction)
174 {
176 [=](ISmaccState * state)
177 {
178 auto bh = state->configure<TOrthogonal, TBehavior>();
179 initializationFunction(*bh, *(static_cast<MostDerived *>(state)));
180 });
181 }
182
183 template <typename TOrthogonal, typename TBehavior>
185 std::function<void(TBehavior & bh)> initializationFunction)
186 {
188 [=](ISmaccState * state)
189 {
190 auto bh = state->configure<TOrthogonal, TBehavior>();
191 initializationFunction(*bh);
192 });
193 }
194
195 template <typename TOrthogonal, typename TBehavior, typename... Args>
196 static void configure_orthogonal(Args &&... args)
197 {
199 [=](ISmaccState * state) { state->configure<TOrthogonal, TBehavior>(args...); });
200 }
201
202 template <
203 typename TStateReactor, typename TOutputEvent, typename TInputEventList, typename... TArgs>
204 static std::shared_ptr<smacc2::introspection::StateReactorHandler> static_createStateReactor(
205 TArgs... args)
206 {
207 auto srh = std::make_shared<smacc2::introspection::StateReactorHandler>(globalNh_);
208 auto srinfo = std::make_shared<SmaccStateReactorInfo>();
209
210 srinfo->stateReactorType = TypeInfo::getTypeInfoFromType<TStateReactor>();
211 srinfo->outputEventType = TypeInfo::getTypeInfoFromType<TOutputEvent>();
212
213 if (srinfo->outputEventType->templateParameters.size() == 2)
214 {
215 srinfo->objectTagType = srinfo->outputEventType->templateParameters[1];
216 }
217 else if (srinfo->outputEventType->templateParameters.size() == 1)
218 {
220 }
221 else
222 {
223 assert(
224 false &&
225 "state reactor output events should have one or two parameters (SourceType, ObjectTag)");
226 }
227
228 // iterate statically on all event sources
229 using boost::mpl::_1;
230 using wrappedList = typename boost::mpl::transform<TInputEventList, _1>::type;
232 boost::mpl::for_each<wrappedList>(op);
233
234 srinfo->srh = srh;
235 srh->srInfo_ = srinfo;
236
237 const std::type_info * tindex = &(typeid(MostDerived)); // get identifier of the current state
238
239 if (!SmaccStateInfo::stateReactorsInfo.count(tindex))
241 std::vector<std::shared_ptr<SmaccStateReactorInfo>>();
242
243 srinfo->factoryFunction = [&, srh, args...](ISmaccState * state)
244 {
245 auto sr =
246 state->createStateReactor<TStateReactor, TOutputEvent, TInputEventList, TArgs...>(args...);
247 srh->configureStateReactor(sr);
248 sr->initialize(state);
249 return sr;
250 };
251
252 SmaccStateInfo::stateReactorsInfo[tindex].push_back(srinfo);
253
254 return srh;
255 }
256
257 template <typename TEventGenerator, typename... TUArgs>
258 static std::shared_ptr<smacc2::introspection::EventGeneratorHandler> static_createEventGenerator(
259 TUArgs... args)
260 {
261 auto egh = std::make_shared<smacc2::introspection::EventGeneratorHandler>();
262 auto eginfo = std::make_shared<SmaccEventGeneratorInfo>();
263 eginfo->eventGeneratorType = TypeInfo::getTypeInfoFromType<TEventGenerator>();
264
265 eginfo->egh = egh;
266 egh->egInfo_ = eginfo;
267
268 const std::type_info * tindex = &(typeid(MostDerived)); // get identifier of the current state
269
270 if (!SmaccStateInfo::eventGeneratorsInfo.count(tindex))
272 std::vector<std::shared_ptr<SmaccEventGeneratorInfo>>();
273
274 eginfo->factoryFunction = [&, egh, args...](ISmaccState * state)
275 {
276 auto eg = state->createEventGenerator<TEventGenerator>(args...);
277 egh->configureEventGenerator(eg);
278 eg->initialize(state);
279
280 eg->template onStateOrthogonalAllocation<MostDerived, TEventGenerator>();
281 eg->template onStateOrthogonalAllocation<MostDerived, TEventGenerator>();
282 return eg;
283 };
284
285 SmaccStateInfo::eventGeneratorsInfo[tindex].push_back(eginfo);
286 return egh;
287 }
288
289 template <typename TStateReactor, typename... TUArgs>
290 static std::shared_ptr<smacc2::introspection::StateReactorHandler> static_createStateReactor_aux(
291 TUArgs... args)
292 {
293 auto srh = std::make_shared<smacc2::introspection::StateReactorHandler>(globalNh_);
294 auto srinfo = std::make_shared<SmaccStateReactorInfo>();
295
296 srinfo->stateReactorType = TypeInfo::getTypeInfoFromType<TStateReactor>();
297 srinfo->srh = srh;
298 srh->srInfo_ = srinfo;
299
300 const std::type_info * tindex = &(typeid(MostDerived)); // get identifier of the current state
301
302 if (!SmaccStateInfo::stateReactorsInfo.count(tindex))
304 std::vector<std::shared_ptr<SmaccStateReactorInfo>>();
305
306 srinfo->factoryFunction = [&, srh, args...](ISmaccState * state)
307 {
308 auto sr = state->createStateReactor<TStateReactor>(args...);
309 srh->configureStateReactor(sr);
310 sr->initialize(state);
311 return sr;
312 };
313
314 SmaccStateInfo::stateReactorsInfo[tindex].push_back(srinfo);
315
316 return srh;
317 }
318
319 void checkWhileLoopConditionAndThrowEvent(bool (MostDerived::*conditionFn)())
320 {
321 auto * thisobject = static_cast<MostDerived *>(this);
322 auto condition = boost::bind(conditionFn, thisobject);
323 bool conditionResult = condition();
324
325 // RCLCPP_INFO("LOOP EVENT CONDITION: %d", conditionResult);
326 if (conditionResult)
327 {
329 }
330 else
331 {
333 }
334 RCLCPP_INFO(getLogger(), "[%s] POST THROW CONDITION", STATE_NAME);
335 }
336
338
340 // The following declarations should be private.
341 // They are only public because many compilers lack template friends.
343 // See base class for documentation
344 typedef typename base_type::outermost_context_base_type outermost_context_base_type;
345 typedef typename base_type::inner_context_ptr_type inner_context_ptr_type;
346 typedef typename base_type::context_ptr_type context_ptr_type;
347 typedef typename base_type::inner_initial_list inner_initial_list;
348
349 static void initial_deep_construct(outermost_context_base_type & outermostContextBase)
350 {
351 deep_construct(&outermostContextBase, outermostContextBase);
352 }
353
354 // See base class for documentation
355 static void deep_construct(
356 const context_ptr_type & pContext, outermost_context_base_type & outermostContextBase)
357 {
358 const inner_context_ptr_type pInnerContext(shallow_construct(pContext, outermostContextBase));
359 base_type::template deep_construct_inner<inner_initial_list>(
360 pInnerContext, outermostContextBase);
361 }
362
364 const context_ptr_type & pContext, outermost_context_base_type & outermostContextBase)
365 {
366 // allocating in memory
367 auto state = new MostDerived(
369 const inner_context_ptr_type pInnerContext(state);
370
371 TRACETOOLS_TRACEPOINT(smacc2_state_onEntry_start, STATE_NAME);
372 state->entryStateInternal();
373 TRACETOOLS_TRACEPOINT(smacc2_state_onEntry_end, STATE_NAME);
374
375 outermostContextBase.add(pInnerContext);
376 return pInnerContext;
377 }
378
379private:
380 template <typename TOrthogonal, typename TBehavior>
382 std::function<void(ISmaccState * state)> initializationFunction)
383 {
385 bhinfo.factoryFunction = initializationFunction;
386
387 bhinfo.behaviorType = &(typeid(TBehavior));
388 bhinfo.orthogonalType = &(typeid(TOrthogonal));
389
390 const std::type_info * tindex = &(typeid(MostDerived));
391 if (!SmaccStateInfo::staticBehaviorInfo.count(tindex))
392 SmaccStateInfo::staticBehaviorInfo[tindex] = std::vector<ClientBehaviorInfoEntry>();
393
394 SmaccStateInfo::staticBehaviorInfo[tindex].push_back(bhinfo);
395 RCLCPP_INFO_STREAM(
396 rclcpp::get_logger("static"), "[states walking] State "
398 << "client behavior count: "
399 << SmaccStateInfo::staticBehaviorInfo[tindex].size());
400 }
401
403 {
404 // finally we go to the derived state onEntry Function
405
406 RCLCPP_DEBUG(getLogger(), "[%s] State object created. Initializing...", STATE_NAME);
407 this->getStateMachine().notifyOnStateEntryStart(static_cast<MostDerived *>(this));
408
409 RCLCPP_DEBUG_STREAM(
410 getLogger(), "[" << smacc2::utils::cleanShortTypeName(typeid(MostDerived)).c_str()
411 << "] creating ros subnode");
412
413 // before dynamic runtimeConfigure, we execute the staticConfigure behavior configurations
414 {
415 RCLCPP_DEBUG(getLogger(), "[%s] -- STATIC STATE DESCRIPTION --", STATE_NAME);
416
417 for (const auto & clientBehavior : SmaccStateInfo::staticBehaviorInfo)
418 {
419 RCLCPP_DEBUG(
420 getLogger(), "[%s] client behavior info: %s", STATE_NAME,
421 demangleSymbol(clientBehavior.first->name()).c_str());
422 for (auto & cbinfo : clientBehavior.second)
423 {
424 RCLCPP_DEBUG(
425 getLogger(), "[%s] client behavior: %s", STATE_NAME,
426 demangleSymbol(cbinfo.behaviorType->name()).c_str());
427 }
428 }
429
430 const std::type_info * tindex = &(typeid(MostDerived));
431 auto & staticDefinedBehaviors = SmaccStateInfo::staticBehaviorInfo[tindex];
432 auto & staticDefinedStateReactors = SmaccStateInfo::stateReactorsInfo[tindex];
433 auto & staticDefinedEventGenerators = SmaccStateInfo::eventGeneratorsInfo[tindex];
434
435 for (auto & ortho : this->getStateMachine().getOrthogonals())
436 {
437 RCLCPP_INFO(
438 getLogger(), "[%s] Initializing orthogonal: %s", STATE_NAME,
439 demangleSymbol(typeid(*ortho.second).name()).c_str());
440 ortho.second->initState(this);
441 }
442
443 RCLCPP_DEBUG_STREAM(
444 getLogger(), "Finding static client behaviors. State Database: "
445 << SmaccStateInfo::staticBehaviorInfo.size() << ". Current state "
446 << cleanShortTypeName(*tindex)
447 << " cbs: " << SmaccStateInfo::staticBehaviorInfo[tindex].size());
448 for (auto & bhinfo : staticDefinedBehaviors)
449 {
450 RCLCPP_DEBUG(
451 getLogger(), "[%s] Creating static client behavior: %s", STATE_NAME,
452 demangleSymbol(bhinfo.behaviorType->name()).c_str());
453 bhinfo.factoryFunction(this);
454 }
455
456 for (auto & sr : staticDefinedStateReactors)
457 {
458 RCLCPP_DEBUG(
459 getLogger(), "[%s] Creating static state reactor: %s", STATE_NAME,
460 sr->stateReactorType->getFullName().c_str());
461 sr->factoryFunction(this);
462 }
463
464 for (auto & eg : staticDefinedEventGenerators)
465 {
466 RCLCPP_DEBUG(
467 getLogger(), "[%s] Creating static event generator: %s", STATE_NAME,
468 eg->eventGeneratorType->getFullName().c_str());
469 eg->factoryFunction(this);
470 }
471
472 RCLCPP_DEBUG(getLogger(), "[%s] ---- END STATIC DESCRIPTION", STATE_NAME);
473 }
474
475 RCLCPP_DEBUG(getLogger(), "[%s] State runtime configuration", STATE_NAME);
476
477 auto * derivedthis = static_cast<MostDerived *>(this);
478
479 // second the orthogonals are internally configured
480 this->getStateMachine().notifyOnRuntimeConfigured(derivedthis);
481
483 // first we runtime configure the state, where we create client behaviors
484 static_cast<MostDerived *>(this)->runtimeConfigure();
485 TRACETOOLS_TRACEPOINT(smacc2_state_onRuntimeConfigure_end, STATE_NAME);
486
488
489 RCLCPP_DEBUG(getLogger(), "[%s] State OnEntry", STATE_NAME);
490
491 static_cast<MostDerived *>(this)->onEntry();
492
493 // here orthogonals and client behaviors are entered OnEntry
494 this->getStateMachine().notifyOnStateEntryEnd(derivedthis);
495 }
496};
497} // namespace smacc2
rclcpp::Node::SharedPtr getNode()
void notifyOnRuntimeConfigurationFinished(StateType *state)
void notifyOnStateExited(StateType *state)
void notifyOnStateEntryEnd(StateType *state)
const SmaccStateMachineInfo & getStateMachineInfo()
void notifyOnStateExiting(StateType *state)
void notifyOnRuntimeConfigured(StateType *state)
void notifyOnStateEntryStart(StateType *state)
std::shared_ptr< rclcpp::Logger > logger_
rclcpp::Logger getLogger()
std::shared_ptr< TBehavior > configure(Args &&... args)
rclcpp::Node::SharedPtr node_
ISmaccState * parentState_
const smacc2::introspection::SmaccStateInfo * stateInfo_
base_type::outermost_context_base_type outermost_context_base_type
static inner_context_ptr_type shallow_construct(const context_ptr_type &pContext, outermost_context_base_type &outermostContextBase)
static std::shared_ptr< smacc2::introspection::StateReactorHandler > static_createStateReactor(TArgs... args)
static void initial_deep_construct(outermost_context_base_type &outermostContextBase)
InnerInitial * smacc_inner_type
void checkWhileLoopConditionAndThrowEvent(bool(MostDerived::*conditionFn)())
static void configure_orthogonal_internal(std::function< void(ISmaccState *state)> initializationFunction)
static std::shared_ptr< smacc2::introspection::EventGeneratorHandler > static_createEventGenerator(TUArgs... args)
virtual ISmaccStateMachine & getStateMachine()
static std::shared_ptr< smacc2::introspection::StateReactorHandler > static_createStateReactor_aux(TUArgs... args)
const smacc2::introspection::SmaccStateInfo * getStateInfo()
sc::simple_state< MostDerived, Context, InnerInitial, historyMode > base_type
bool getGlobalSMData(std::string name, T &ret)
base_type::inner_initial_list inner_initial_list
static void deep_construct(const context_ptr_type &pContext, outermost_context_base_type &outermostContextBase)
virtual ISmaccState * getParentState()
SmaccState(my_context ctx)
base_type::context_ptr_type context_ptr_type
std::string getName() override
void setGlobalSMData(std::string name, T value)
base_type::inner_context_ptr_type inner_context_ptr_type
static void configure_orthogonal_runtime(std::function< void(TBehavior &bh)> initializationFunction)
void requiresComponent(SmaccComponentType *&storage)
static void configure_orthogonal_runtime(std::function< void(TBehavior &bh, MostDerived &)> initializationFunction)
static void configure_orthogonal(Args &&... args)
context_type::state_iterator state_iterator
Context::inner_context_type context_type
static std::map< const std::type_info *, std::vector< std::shared_ptr< SmaccStateReactorInfo > > > stateReactorsInfo
static std::map< const std::type_info *, std::vector< std::shared_ptr< SmaccEventGeneratorInfo > > > eventGeneratorsInfo
static std::map< const std::type_info *, std::vector< ClientBehaviorInfoEntry > > staticBehaviorInfo
static TypeInfo::Ptr getTypeInfoFromType()
rclcpp::Node::SharedPtr globalNh_
std::string demangleSymbol()
std::string cleanShortTypeName()
Definition common.hpp:61
void standardOnExit(TState &st, std::true_type)
#define STATE_NAME
my_context(typename base_type::context_ptr_type pContext)
base_type::context_ptr_type pContext_
std::function< void(smacc2::ISmaccState *)> factoryFunction
smacc2_state_onExit_end
smacc2_state_onRuntimeConfigure_start
smacc2_state_onEntry_end
smacc2_state_onRuntimeConfigure_end
smacc2_state_onEntry_start
smacc2_state_onExit_start