YARP
Yet Another Robot Platform
 
Loading...
Searching...
No Matches
Module.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2006-2021 Istituto Italiano di Tecnologia (IIT)
3 * SPDX-License-Identifier: LGPL-2.1-or-later
4 */
5
6#include "Module.h"
7
13
14#include <yarp/conf/system.h>
15#include <yarp/os/LogStream.h>
17#include <yarp/os/RpcServer.h>
18
19#if defined(YARP_HAS_EXECINFO_H) && !defined(__APPLE__) && !defined(__arm__) && !defined(__aarch64__) && !defined(__PPC__)
20# include <csignal>
21# include <cstring>
22# include <execinfo.h>
23#endif
24
25namespace {
26YARP_LOG_COMPONENT(YRI, "yarp.yri")
27}
28
30{
31public:
34
35#if defined(YARP_HAS_EXECINFO_H) && !defined(__APPLE__) && !defined(__arm__) && !defined(__aarch64__) && !defined(__PPC__)
36 static struct sigaction old_action;
37 static void sigsegv_handler(int nSignum, siginfo_t* si, void* vcontext);
38#endif
39
40 Module * const parent;
44 bool closed;
45 bool closeOk;
47};
48
49#if defined(YARP_HAS_EXECINFO_H) && !defined(__APPLE__) && !defined(__arm__) && !defined(__aarch64__) && !defined(__PPC__)
50struct sigaction yarprobotinterface::Module::Private::old_action;
51#endif
52
54 parent(parent),
55 interruptReceived(0),
56 closed(false),
57 closeOk(true)
58{
59}
60
62
63#if defined(YARP_HAS_EXECINFO_H) && !defined(__APPLE__) && !defined(__arm__) && !defined(__aarch64__) && !defined(__PPC__)
64void yarprobotinterface::Module::Private::sigsegv_handler(int nSignum, siginfo_t* si, void* vcontext)
65{
66 auto context = reinterpret_cast<ucontext_t*>(vcontext);
67#if defined(__x86_64__)
68 context->uc_mcontext.gregs[REG_RIP]++;
69#else
70 context->uc_mcontext.gregs[REG_EIP]++;
71#endif
72
73 const size_t max_depth = 100;
74 size_t stack_depth;
75 void* stack_addrs[max_depth];
76 char** stack_strings;
77 stack_depth = backtrace(stack_addrs, max_depth);
78 stack_strings = backtrace_symbols(stack_addrs, stack_depth);
79
80 yError("yarprobotinterface intercepted a segmentation fault caused by a faulty plugin:");
81 yError("%s\n", stack_strings[2]);
82 yarp_print_trace(stderr, __FILE__, __LINE__);
83
84 // Free memory allocated by backtrace_symbols()
85 free(stack_strings);
86
87 // Restore original action
88 sigaction(SIGSEGV, &old_action, nullptr);
89}
90#endif
91
93 mPriv(new Private(this))
94{
95#if defined(YARP_HAS_EXECINFO_H) && !defined(__APPLE__) && !defined(__arm__) && !defined(__aarch64__) && !defined(__PPC__)
96 struct sigaction action;
97 memset(&action, 0, sizeof(struct sigaction));
98 memset(&Private::old_action, 0, sizeof(struct sigaction));
99 action.sa_flags = SA_SIGINFO;
100 action.sa_sigaction = Private::sigsegv_handler;
101 sigaction(SIGSEGV, &action, &Private::old_action);
102#endif
103}
104
106{
107 delete mPriv;
108}
109
111{
112 if (!rf.check("config")) {
113 yFatal() << "Missing \"config\" argument";
114 }
115
116 const std::string& filename = rf.findFile("config");
117 yTrace() << "Reading robot config file" << filename;
118
119 bool verbosity = rf.check("verbose");
120 bool deprecated = rf.check("allow-deprecated-dtd");
121 bool dryrun = rf.check("dryrun");
122 mPriv->autocloseAfterStart = rf.check("autocloseAfterStart");
123
125 reader.setVerbose(verbosity);
126 reader.setEnableDeprecated(deprecated);
127
128 // Prepare configuration for sub-devices
129 yarp::os::Property config;
130 config.fromString(rf.toString());
131 // The --config option is consumed by yarprobotinterface, and never
132 // forwarded to the devices)
133 config.unput("config");
134
135 yarp::robotinterface::XMLReaderResult result = reader.getRobotFromFile(filename, config);
136
137 if (!result.parsingIsSuccessful) {
138 yFatal() << "Config file " << filename << " not parsed correctly.";
139 }
140
141 mPriv->robot = std::move(result.robot);
142 // yDebug() << mPriv->robot;
143
144 // User can use YARP_PORT_PREFIX environment variable to override
145 // the default name, so we don't care of handling the --name
146 // argument
147 setName(mPriv->robot.portprefix().c_str());
148
149 mPriv->robot.setVerbose(verbosity);
150 mPriv->robot.setAllowDeprecatedDevices(rf.check("allow-deprecated-devices"));
151 mPriv->robot.setDryRun(dryrun);
152
153 std::string portprefix = mPriv->robot.portprefix();
154 if (portprefix[0] != '/') {
155 yWarning() <<
156 "*************************************************************************************\n"
157 "* yarprobotinterface 'portprefix' parameter does not follow convention, *\n"
158 "* it MUST start with a leading '/' since it is used as the full prefix port name *\n"
159 "* name: full port prefix name with leading '/', e.g. /robotName *\n"
160 "* A temporary automatic fix will be done for you, but please fix your config file *\n"
161 "*************************************************************************************";
162 portprefix = "/" + portprefix;
163 }
164
165 std::string rpcPortName(portprefix + "/yarprobotinterface");
166 mPriv->rpcPort.open(rpcPortName);
167 attach(mPriv->rpcPort);
168
169 // Enter startup phase
170 if (!mPriv->robot.enterPhase(yarp::robotinterface::ActionPhaseStartup) ||
171 !mPriv->robot.enterPhase(yarp::robotinterface::ActionPhaseRun)) {
172 yError() << "Error in" << ActionPhaseToString(mPriv->robot.currentPhase()) << "phase... see previous messages for more info";
173 // stopModule() calls interruptModule() internally.
174 // This ensure that interrupt1 phase actions (i.e. detach) are
175 // performed before destroying the devices when we call close();
176 stopModule();
177 // According to robotology/yarp#482, close() is not called by
178 // runModule(rf), and the user is supposed to leave everything
179 // clean.
180 close();
181 return false;
182 }
183 return true;
184}
185
187{
188 return 60;
189}
190
192{
193 yCDebug(YRI) << "yarprobotinterface running happily";
194 if (mPriv->autocloseAfterStart && mPriv->robot.currentPhase() == yarp::robotinterface::ActionPhaseRun)
195 {
196 yCInfo(YRI) << "`autocloseAfterStart` option selected. Calling close()";
197 // close();
198 return false;
199 }
200
201 return true;
202}
203
205{
206 mPriv->interruptReceived++;
207
208 yCWarning(YRI) << "Interrupt #" << mPriv->interruptReceived << "# received.";
209
210 mPriv->robot.interrupt();
211
212 // In the first interrupt, after sending the interrupt() command
213 // to the robot we exit the callback. In the close() method then
214 // we proceed with the interupt1 phase, where we wait for all the
215 // threads already started are joined, and finally we start the
216 // interrupt1 actions.
217 // In the second and third interrupts, we enter the interrupt2
218 // phase in the callback. This means that in Robot, we cannot
219 // wait for the other threads using join().
220 switch (mPriv->interruptReceived) {
221 case 1:
222 break;
223 case 2:
224 if (!mPriv->robot.enterPhase(yarp::robotinterface::ActionPhaseInterrupt2)) {
225 yCError(YRI) << "Error in" << ActionPhaseToString(yarp::robotinterface::ActionPhaseInterrupt2) << "phase... see previous messages for more info";
226 return false;
227 }
228 break;
229 case 3:
230 if (!mPriv->robot.enterPhase(yarp::robotinterface::ActionPhaseInterrupt3)) {
231 yCError(YRI) << "Error in" << ActionPhaseToString(yarp::robotinterface::ActionPhaseInterrupt3) << "phase... see previous messages for more info";
232 return false;
233 }
234 break;
235 default:
236 return false;
237 }
238
239 return true;
240}
241
243{
244 if (mPriv->closed) {
245 return mPriv->closeOk;
246 }
247 mPriv->closed = true;
248
249 // If called from the first interrupt, enter the corresponding
250 // interrupt phase.
251 switch (mPriv->interruptReceived) {
252 case 1:
253 if (!mPriv->robot.enterPhase(yarp::robotinterface::ActionPhaseInterrupt1)) {
254 yCError(YRI) << "Error in" << ActionPhaseToString(yarp::robotinterface::ActionPhaseInterrupt1) << "phase... see previous messages for more info";
255 mPriv->closeOk = false;
256 }
257 break;
258 case 2:
259 case 3:
260 break;
261 default:
262 mPriv->closeOk = false;
263 }
264
265 // Finally call the shutdown phase.
266 if (!mPriv->robot.enterPhase(yarp::robotinterface::ActionPhaseShutdown)) {
267 yCError(YRI) << "Error in" << ActionPhaseToString(yarp::robotinterface::ActionPhaseShutdown) << "phase... see previous messages for more info";
268 mPriv->closeOk = false;
269 }
270
271 mPriv->rpcPort.interrupt();
272 mPriv->rpcPort.close();
273 return mPriv->closeOk;
274}
275
277{
278 return this->yarp().attachAsServer(source);
279}
280
281
283{
284 return ActionPhaseToString(mPriv->robot.currentPhase());
285}
286
288{
289 return mPriv->robot.currentLevel();
290}
291
293{
294 return (mPriv->robot.currentPhase() == yarp::robotinterface::ActionPhaseRun ? true : false);
295}
296
298{
299 return mPriv->robot.name();
300}
301
303{
304 stopModule();
305 return "bye";
306}
void yarp_print_trace(FILE *out, const char *file, unsigned int line)
Low level function for printing a stack trace, if implemented (ACE or gcc/Linux).
Definition Log.cpp:1119
#define yError(...)
Definition Log.h:361
#define yTrace(...)
Definition Log.h:231
#define yWarning(...)
Definition Log.h:340
#define yFatal(...)
Definition Log.h:382
A class for storing options and configuration information.
Definition Property.h:33
void fromString(const std::string &txt, bool wipe=true)
Interprets a string as a list of properties.
void unput(const std::string &key)
Remove the association from the given key to a value, if present.
Helper class for finding config files and other external resources.
bool check(const std::string &key) const override
Check if there exists a property of the given name.
std::string toString() const override
Return a standard text representation of the content of the object.
std::string findFile(const std::string &name)
Find the full path to a file.
A port that is specialized as an RPC server.
Definition RpcServer.h:23
Result of the parsing of yarp::robotinterface::XMLReader.
Definition XMLReader.h:26
bool parsingIsSuccessful
True if the parsing was successful, false otherwise.
Definition XMLReader.h:38
Robot robot
If parsingIsSuccessful is true, contains a valid robot instance.
Definition XMLReader.h:43
Class to read an XML file.
Definition XMLReader.h:52
XMLReaderResult getRobotFromFile(const std::string &filename, const yarp::os::Searchable &config=yarp::os::Property())
Parse the XML description of a robotinterface from a file.
Definition XMLReader.cpp:78
void setEnableDeprecated(bool enab)
Definition XMLReader.cpp:73
void setVerbose(bool verbose)
Definition XMLReader.cpp:68
static void sigsegv_handler(int nSignum, siginfo_t *si, void *vcontext)
Definition Module.cpp:64
static struct sigaction old_action
Definition Module.cpp:36
yarp::os::RpcServer rpcPort
Definition Module.cpp:43
yarp::robotinterface::Robot robot
Definition Module.cpp:41
std::string get_robot() override
Returns robot name.
Definition Module.cpp:297
bool updateModule() override
Override this to do whatever your module needs to do.
Definition Module.cpp:191
std::string get_phase() override
Returns current phase.
Definition Module.cpp:282
double getPeriod() override
You can override this to control the approximate periodicity at which updateModule() is called by run...
Definition Module.cpp:186
bool close() override
Close function.
Definition Module.cpp:242
bool attach(yarp::os::RpcServer &source) override
Make any input from an RpcServer object go to the respond() method.
Definition Module.cpp:276
bool configure(yarp::os::ResourceFinder &rf) override
Configure the module, pass a ResourceFinder object to the module.
Definition Module.cpp:110
int32_t get_level() override
Returns current level.
Definition Module.cpp:287
bool is_ready() override
Returns true if yarprobotinterface is ready (all startup actions performed and no interrupt called).
Definition Module.cpp:292
std::string quit() override
Closes yarprobotinterface.
Definition Module.cpp:302
bool interruptModule() override
Try to halt any ongoing operations by threads managed by the module.
Definition Module.cpp:204
#define yCInfo(component,...)
#define yCError(component,...)
#define yCWarning(component,...)
#define yCDebug(component,...)
#define YARP_LOG_COMPONENT(name,...)
The main, catch-all namespace for YARP.
Definition dirs.h:16