RobotUnitModuleBase.cpp
Go to the documentation of this file.
1/*
2 * This file is part of ArmarX.
3 *
4 * ArmarX is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * ArmarX is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * @package RobotAPI::ArmarXObjects::RobotUnit
17 * @author Raphael Grimm ( raphael dot grimm at kit dot edu )
18 * @date 2018
19 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
20 * GNU General Public License
21 */
22
23#include "RobotUnitModuleBase.h"
24
25#include <sstream>
26
27#include <VirtualRobot/Robot.h>
28
32
33#include "RobotUnitModules.h"
34
35namespace armarx
36{
37 std::string
39 {
40 switch (s)
41 {
43 return "RobotUnitState::InitializingComponent";
45 return "RobotUnitState::InitializingDevices";
47 return "RobotUnitState::InitializingUnits";
49 return "RobotUnitState::InitializingControlThread";
51 return "RobotUnitState::Running";
53 return "RobotUnitState::Exiting";
54 }
55 throw std::invalid_argument{"Unknown state " + to_string(static_cast<std::size_t>(s)) +
56 "\nStacktrace\n" + LogSender::CreateBackTrace()};
57 }
58} // namespace armarx
59
61{
62 // //////////////////////////////////////////////////////////////////////////// //
63 // /////////////////////////// call for each module /////////////////////////// //
64 // //////////////////////////////////////////////////////////////////////////// //
65#define cast_to_and_call(Type, fn, rethrow) \
66 ARMARX_TRACE; \
67 try \
68 { \
69 dynamic_cast<Type*>(this)->Type::fn; \
70 } \
71 catch (Ice::Exception & e) \
72 { \
73 ARMARX_ERROR << "exception in " << #Type "::" #fn << "!\nwhat:\n" \
74 << e.what() << "\n\tid : " << e.ice_id() << "\n\tfile : " << e.ice_file() \
75 << "\n\tline : " << e.ice_line() << "\n\tstack: " << e.ice_stackTrace(); \
76 if (rethrow) \
77 { \
78 throw; \
79 } \
80 } \
81 catch (std::exception & e) \
82 { \
83 ARMARX_ERROR << "exception in " << #Type "::" #fn << "!\nwhat:\n" << e.what(); \
84 if (rethrow) \
85 { \
86 throw; \
87 } \
88 } \
89 catch (...) \
90 { \
91 ARMARX_ERROR << "exception in " << #Type "::" #fn << "!"; \
92 if (rethrow) \
93 { \
94 throw; \
95 } \
96 }
97
98#define for_each_module_apply(r, data, elem) data(elem)
99#define for_each_module(macro) \
100 BOOST_PP_SEQ_FOR_EACH(for_each_module_apply, macro, BOOST_PP_VARIADIC_TO_SEQ(RobotUnitModules))
101
102#define check_base(Type) \
103 static_assert(std::is_base_of<ModuleBase, Type>::value, \
104 "The RobotUnitModule '" #Type "' has to derived ModuleBase");
106#undef check_base
107
109 {
110#define check_deriving(Type) \
111 ARMARX_CHECK_NOT_NULL(dynamic_cast<const Type*>(this)) \
112 << "This class does not derive from " << GetTypeString<Type>();
114#undef check_deriving
115 }
116
117 // //////////////////////////////////////////////////////////////////////////// //
118 // ManagedIceObject life cycle
119 void
121 {
124 auto guard = getGuard();
126 ARMARX_IMPORTANT << "Starting initialization of RobotUnitModule '" << getName();
127 cast_to_and_call(::armarx::RobotUnitModule::RobotData, _initVirtualRobot(), true);
128
129#define call_module_hook(Type) \
130 cast_to_and_call(::armarx::RobotUnitModule::Type, _preOnInitRobotUnit(), true)
132#undef call_module_hook
133
135
136#define call_module_hook(Type) \
137 cast_to_and_call(::armarx::RobotUnitModule::Type, _postOnInitRobotUnit(), true)
139#undef call_module_hook
140
142 }
143
144 void
146 {
149
150#define call_module_hook(Type) \
151 cast_to_and_call(::armarx::RobotUnitModule::Type, _preOnConnectRobotUnit(), true)
153#undef call_module_hook
154
156
157#define call_module_hook(Type) \
158 cast_to_and_call(::armarx::RobotUnitModule::Type, _postOnConnectRobotUnit(), true)
160#undef call_module_hook
161 }
162
163 void
165 {
168
169#define call_module_hook(Type) \
170 cast_to_and_call(::armarx::RobotUnitModule::Type, _preOnDisconnectRobotUnit(), true)
172#undef call_module_hook
173
175
176#define call_module_hook(Type) \
177 cast_to_and_call(::armarx::RobotUnitModule::Type, _postOnDisconnectRobotUnit(), true)
179#undef call_module_hook
180 }
181
182 void
184 {
187 auto guard = getGuard();
188
190
191#define call_module_hook(Type) \
192 cast_to_and_call(::armarx::RobotUnitModule::Type, _preOnExitRobotUnit(), false)
194#undef call_module_hook
195
197
198#define call_module_hook(Type) \
199 cast_to_and_call(::armarx::RobotUnitModule::Type, _postOnExitRobotUnit(), false)
201#undef call_module_hook
202 }
203
204 // //////////////////////////////////////////////////////////////////////////// //
205 // other ManagedIceObject / Component methods
206 void
207 ModuleBase::componentPropertiesUpdated(const std::set<std::string>& changedProperties)
208 {
209#define call_module_hook(Type) \
210 cast_to_and_call( \
211 ::armarx::RobotUnitModule::Type, _componentPropertiesUpdated(changedProperties), true)
213#undef call_module_hook
214 }
215
216 void
218 {
219#define call_module_hook(Type) \
220 cast_to_and_call(::armarx::RobotUnitModule::Type, _icePropertiesInitialized(), true)
222#undef call_module_hook
223 }
224
225 // //////////////////////////////////////////////////////////////////////////// //
226 // RobotUnit life cycle
227 void
229 {
232 auto guard = getGuard();
234
235#define call_module_hook(Type) \
236 cast_to_and_call(::armarx::RobotUnitModule::Type, _preFinishComponentInitialization(), true)
238#undef call_module_hook
239
240 setRobotUnitState(RobotUnitState::InitializingDevices);
241
242#define call_module_hook(Type) \
243 cast_to_and_call(::armarx::RobotUnitModule::Type, _postFinishComponentInitialization(), true)
245#undef call_module_hook
246 }
247
248 void
250 {
253 auto guard = getGuard();
255
256#define call_module_hook(Type) \
257 cast_to_and_call(::armarx::RobotUnitModule::Type, _preFinishDeviceInitialization(), true)
259#undef call_module_hook
260
261 setRobotUnitState(RobotUnitState::InitializingUnits);
262
263#define call_module_hook(Type) \
264 cast_to_and_call(::armarx::RobotUnitModule::Type, _postFinishDeviceInitialization(), true)
266#undef call_module_hook
267 }
268
269 void
271 {
274 auto guard = getGuard();
276
277#define call_module_hook(Type) \
278 cast_to_and_call(::armarx::RobotUnitModule::Type, _preFinishUnitInitialization(), true)
280#undef call_module_hook
281
283
284#define call_module_hook(Type) \
285 cast_to_and_call(::armarx::RobotUnitModule::Type, _postFinishUnitInitialization(), true)
287#undef call_module_hook
288 }
289
290 void
292 {
295 auto guard = getGuard();
297
298#define call_module_hook(Type) \
299 cast_to_and_call(::armarx::RobotUnitModule::Type, _preFinishControlThreadInitialization(), true)
301#undef call_module_hook
302
303 setRobotUnitState(RobotUnitState::Running);
304
305#define call_module_hook(Type) \
306 cast_to_and_call( \
307 ::armarx::RobotUnitModule::Type, _postFinishControlThreadInitialization(), true)
309#undef call_module_hook
310 }
311
312 void
314 {
317 shutDown();
318 GuardType guard{dataMutex}; //DO NOT USE getGuard here!
320 {
321 ARMARX_ERROR << "called finishRunning when state was "
323 }
324
325#define call_module_hook(Type) \
326 cast_to_and_call(::armarx::RobotUnitModule::Type, _preFinishRunning(), false)
328#undef call_module_hook
329
330 setRobotUnitState(RobotUnitState::Exiting);
332
333#define call_module_hook(Type) \
334 cast_to_and_call(::armarx::RobotUnitModule::Type, _postFinishRunning(), false)
336#undef call_module_hook
337 }
338
339#undef for_each_module_apply
340#undef for_each_module
341#undef cast_to_and_call
342 // //////////////////////////////////////////////////////////////////////////// //
343 // ////////////////////////////////// other /////////////////////////////////// //
344 // //////////////////////////////////////////////////////////////////////////// //
345
346 const std::set<RobotUnitState> ModuleBase::DevicesReadyStates{
350
351 void
352 ModuleBase::setRobotUnitState(RobotUnitState to)
353 {
355 auto guard = getGuard();
356
357 const auto transitionErrorMessage = [=, this](RobotUnitState expectedFrom)
358 {
359 return "Can't switch to state " + to_string(to) + " from " + to_string(state) +
360 "! The expected source state is " + to_string(expectedFrom) + ".";
361 };
362 const auto transitionErrorThrow = [=, this](RobotUnitState expectedFrom)
363 {
364 if (state != expectedFrom)
365 {
366 const auto msg = transitionErrorMessage(expectedFrom);
367 ARMARX_ERROR << msg;
368 throw std::invalid_argument{msg};
369 }
370 };
371
372 switch (to)
373 {
375 throw std::invalid_argument{
376 "Can't switch to state RobotUnitState::InitializingComponent"};
378 transitionErrorThrow(RobotUnitState::InitializingComponent);
379 break;
381 transitionErrorThrow(RobotUnitState::InitializingDevices);
382 break;
384 transitionErrorThrow(RobotUnitState::InitializingUnits);
385 break;
387 transitionErrorThrow(RobotUnitState::InitializingControlThread);
388 break;
390 if (state != RobotUnitState::Running)
391 {
392 ARMARX_ERROR << transitionErrorMessage(RobotUnitState::Running);
393 }
394 break;
395 default:
396 throw std::invalid_argument{"setRobotUnitState: Unknown target state " +
397 to_string(static_cast<std::size_t>(to)) +
398 "\nStacktrace\n" + LogSender::CreateBackTrace()};
399 }
400 ARMARX_INFO << "switching state from " << to_string(state) << " to " << to_string(to);
401 state = to;
402 }
403
404 bool
406 {
407 return DevicesReadyStates.count(state);
408 }
409
410 bool
412 {
414 std::this_thread::get_id() == _module<ControlThread>().getControlThreadId();
415 }
416
417 void
418 ModuleBase::throwIfInControlThread(const std::string& fnc) const
419 {
421 if (inControlThread())
422 {
423 std::stringstream str;
424 str << "Called function '" << fnc << "' in the Control Thread\nStack trace:\n"
426 ARMARX_ERROR << str.str();
427 throw LogicError{str.str()};
428 }
429 }
430
433 {
435 if (inControlThread())
436 {
437 throw LogicError{"Attempted to lock mutex in Control Thread\nStack trace:\n" +
439 }
440 GuardType guard{dataMutex, std::defer_lock};
441 if (guard.try_lock())
442 {
443 return guard;
444 }
445 while (!guard.try_lock_for(std::chrono::microseconds{100}))
446 {
447 if (isShuttingDown())
448 {
449 throw LogicError{"Attempting to lock mutex during shutdown\nStacktrace\n" +
451 }
452 }
453
454 return guard;
455 }
456
457 void
458 ModuleBase::throwIfStateIsNot(const std::set<RobotUnitState>& stateSet,
459 const std::string& fnc,
460 bool onlyWarn) const
461 {
463 if (!stateSet.count(state))
464 {
465 std::stringstream ss;
466 ss << fnc << ": Can't be called if state is not in {";
467 bool fst = true;
468 for (RobotUnitState st : stateSet)
469 {
470 if (!fst)
471 {
472 ss << ", ";
473 }
474 ss << st;
475 fst = false;
476 }
477 ss << "} (current state " << getRobotUnitState() << ")\n"
478 << "Stacktrace:\n"
480 ARMARX_ERROR << ss.str();
481 if (!onlyWarn)
482 {
483 throw LogicError{ss.str()};
484 }
485 }
486 }
487
488 void
489 ModuleBase::throwIfStateIsNot(RobotUnitState s, const std::string& fnc, bool onlyWarn) const
490 {
491 throwIfStateIsNot(std::set<RobotUnitState>{s}, fnc, onlyWarn);
492 }
493
494 void
495 ModuleBase::throwIfStateIs(const std::set<RobotUnitState>& stateSet,
496 const std::string& fnc,
497 bool onlyWarn) const
498 {
500 if (stateSet.count(state))
501 {
502 std::stringstream ss;
503 ss << fnc << ": Can't be called if state is in {";
504 bool fst = true;
505 for (RobotUnitState st : stateSet)
506 {
507 if (!fst)
508 {
509 ss << ", ";
510 }
511 ss << st;
512 fst = false;
513 }
514 ss << "} (current state " << getRobotUnitState() << ")\n"
515 << "Stacktrace:\n"
517 ARMARX_ERROR << ss.str();
518 if (!onlyWarn)
519 {
520 throw LogicError{ss.str()};
521 }
522 }
523 }
524
525 void
526 ModuleBase::throwIfStateIs(RobotUnitState s, const std::string& fnc, bool onlyWarn) const
527 {
528 throwIfStateIs(std::set<RobotUnitState>{s}, fnc, onlyWarn);
529 }
530
531 void
532 ModuleBase::throwIfDevicesNotReady(const std::string& fnc) const
533 {
534 throwIfStateIsNot(DevicesReadyStates, fnc);
535 }
536
537 std::atomic<ModuleBase*> ModuleBase::Instance_{nullptr};
538
540 {
542 if (Instance_ && this != Instance_)
543 {
544 ARMARX_FATAL << "This class is a Singleton. It was already instantiated. (Instance = "
545 << Instance_ << ", this = " << this << ")";
546 std::terminate();
547 }
548 Instance_ = this;
549 }
550
551} // namespace armarx::RobotUnitModule
#define check_base(Type)
#define check_deriving(Type)
#define call_module_hook(Type)
#define for_each_module(macro)
#define cast_to_and_call(Type, fn, rethrow)
std::string str(const T &t)
static std::string CreateBackTrace(int linesToSkip=1)
std::string getName() const
Retrieve name of object.
virtual void finishDeviceInitialization()
Transition InitializingDevices -> InitializingUnits.
RobotUnitState getRobotUnitState() const
Returns the RobotUnit's State.
void shutDown()
Requests the RobotUnit to shut down.
void componentPropertiesUpdated(const std::set< std::string > &changedProperties) override
virtual void finishRunning()
Transition Running -> Exiting.
T & _module()
Returns this as ref to the given type.
bool areDevicesReady() const
Returns whether Devices are ready.
virtual void finishUnitInitialization()
Transition InitializingUnits -> RobotUnitState::WaitingForRTThreadInitialization.
void checkDerivedClasses() const
Checks whether the implementing class derives all modules.
GuardType getGuard() const
Returns a guard to the RobotUnits mutex.
void throwIfInControlThread(const std::string &fnc) const
Throws if the current thread is the ControlThread.
virtual void finishControlThreadInitialization()
Transition InitializingControlThread -> Running.
void onInitComponent() final override
void throwIfStateIsNot(const std::set< RobotUnitState > &stateSet, const std::string &fnc, bool onlyWarn=false) const
Throws an exception if the current state is not in.
void throwIfStateIs(const std::set< RobotUnitState > &stateSet, const std::string &fnc, bool onlyWarn=false) const
Throws an exception if the current state is in.
virtual void onConnectRobotUnit()
called in onConnectComponent
virtual void onDisconnectRobotUnit()
called in onDisconnecComponent
bool inControlThread() const
Returns whether the current thread is the ControlThread.
virtual void onInitRobotUnit()
called in onInitComponent
virtual void onExitRobotUnit()
called in onExitComponent before calling finishRunning
virtual void joinControlThread()=0
Implementations have to join their ControlThread in this hook. (used by RobotUnit::finishRunning())
bool isShuttingDown() const
Returns whether the RobotUnit is shutting down.
void throwIfDevicesNotReady(const std::string &fnc) const
Throws if the Devices are not ready.
std::unique_lock< MutexType > GuardType
virtual void finishComponentInitialization()
Transition InitializingComponent -> InitializingDevices.
This Module holds all high-level data about the robot.
#define ARMARX_INFO
The normal logging level.
Definition Logging.h:181
#define ARMARX_IMPORTANT
The logging level for always important information, but expected behaviour (in contrast to ARMARX_WAR...
Definition Logging.h:190
#define ARMARX_FATAL
The logging level for unexpected behaviour, that will lead to a seriously malfunctioning program and ...
Definition Logging.h:199
#define ARMARX_ERROR
The logging level for unexpected behaviour, that must be fixed.
Definition Logging.h:196
This file offers overloads of toIce() and fromIce() functions for STL container types.
RobotUnitState
The current state of the multi step initialization of a RobotUnit.
const std::string & to_string(const std::string &s)
#define ARMARX_TRACE
Definition trace.h:77