YARP
Yet Another Robot Platform
 
Loading...
Searching...
No Matches
RFModule.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2006-2021 Istituto Italiano di Tecnologia (IIT)
3 * SPDX-FileCopyrightText: 2006-2010 RobotCub Consortium
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7#include <yarp/os/RFModule.h>
8
9#include <yarp/os/Bottle.h>
11#include <yarp/os/Network.h>
12#include <yarp/os/Os.h>
13#include <yarp/os/Thread.h>
14#include <yarp/os/Time.h>
15#include <yarp/os/Vocab.h>
20
21#include <cstdio>
22#include <cstdlib>
23#include <string>
24
25using namespace yarp::os;
26using namespace yarp::os::impl;
27
28namespace {
29YARP_OS_LOG_COMPONENT(RFMODULE, "yarp.os.RFModule")
30}
31
34 public yarp::os::Thread
35{
36private:
37 RFModule& owner;
38 bool attachedToPort;
39 bool attachedTerminal;
40
41public:
49
50
52 owner(owner),
53 attachedToPort(false),
54 attachedTerminal(false)
55 {
56 }
57
58
59 void run() override
60 {
61 yCInfo(RFMODULE, "Listening to terminal (type \"quit\" to stop module).");
62 bool isEof = false;
63 while (!(isEof || isStopping() || owner.isStopping())) {
65 if (!isEof) {
66 Bottle cmd(str);
67 Bottle reply;
68 bool ok = owner.safeRespond(cmd, reply);
69 if (ok) {
70 yCDebug(RFMODULE, "ALL: %s\n", reply.toString().c_str());
71 yCDebug(RFMODULE, "ITEM 1: %s\n", reply.get(0).toString().c_str());
72 if (reply.get(0).toString() == "help") {
73 for (size_t i = 0; i < reply.size(); i++) {
74 yCInfo(RFMODULE, "%s.", reply.get(i).toString().c_str());
75 }
76 } else {
77 yCInfo(RFMODULE, "%s.", reply.toString().c_str());
78 }
79 } else {
80 yCInfo(RFMODULE, "Command not understood -- %s.", str.c_str());
81 }
82 }
83 }
84 yCDebug(RFMODULE, "terminal shutting down\n");
85 //owner.interruptModule();
86 }
87
88
89 bool attach(yarp::os::Port& source)
90 {
91 attachedToPort = true;
92 source.setReader(*this);
93 return true;
94 }
95
96
98 {
99 attachedToPort = true;
100 source.setReader(*this);
101 return true;
102 }
103
104
106 {
107 attachedTerminal = true;
108
110 return true;
111 }
112
113
115 {
116 return attachedTerminal;
117 }
118
119
121 {
122 yCWarning(RFMODULE, "Critical: stopping thread, this might hang.");
123 Thread::stop();
124 yCWarning(RFMODULE, "done!");
125 return true;
126 }
127};
128
130{
131 Bottle cmd;
132 Bottle response;
133 if (!cmd.read(connection)) {
134 return false;
135 }
136 yCDebug(RFMODULE, "command received: %s\n", cmd.toString().c_str());
137
138 bool result = owner.safeRespond(cmd, response);
139 if (response.size() >= 1) {
140 ConnectionWriter* writer = connection.getWriter();
141 if (writer != nullptr) {
142 if (response.get(0).toString() == "many" && writer->isTextMode()) {
143 for (size_t i = 1; i < response.size(); i++) {
144 Value& v = response.get(i);
145 if (v.isList()) {
146 v.asList()->write(*writer);
147 } else {
148 Bottle b;
149 b.add(v);
150 b.write(*writer);
151 }
152 }
153 } else {
154 response.write(*writer);
155 }
156 yCDebug(RFMODULE, "response sent: %s\n", response.toString().c_str());
157 }
158 }
159 return result;
160}
161
162
164{
165private:
166 RFModule& owner;
167
168public:
170 owner(owner)
171 {
172 }
173
174 void run() override
175 {
176 owner.runModule();
177 }
178};
179
180
182{
183private:
184 RFModule& owner;
185 bool singleton_run_module;
186
187public:
190
192 owner(owner),
193 singleton_run_module(false),
194 respond_handler(nullptr),
195 threaded_handler(nullptr)
196 {
198 }
199
201 {
202 delete respond_handler;
203 delete threaded_handler;
204 }
205
207 {
209
210 return threaded_handler != nullptr;
211 }
212
214 {
215 delete threaded_handler;
216 threaded_handler = nullptr;
217 }
218
219
221 {
222 return singleton_run_module;
223 }
225 {
226 singleton_run_module = true;
227 }
228};
229
230
231static RFModule* module = nullptr;
232
234{
235 if (mPriv->threaded_handler != nullptr) {
236 return mPriv->threaded_handler->getKey();
237 }
239}
240
241static void handler(int sig)
242{
243 YARP_UNUSED(sig);
244 static int ct = 0;
245 ct++;
247 if (ct > 3) {
248 yCInfo(RFMODULE, "Aborting (calling abort())...");
249 std::abort();
250 }
251 yCInfo(RFMODULE, "[try %d of 3] Trying to shut down.", ct);
252
253 if (module != nullptr) {
254 module->stopModule(false);
255 }
256
257#if defined(_WIN32)
258 // on windows we need to reset the handler after being called, otherwise it
259 // will not be called anymore.
260 // see http://www.geeksforgeeks.org/write-a-c-program-that-doesnt-terminate-when-ctrlc-is-pressed/
261
262 // Additionally, from
263 // http://www.linuxprogrammingblog.com/all-about-linux-signals?page=show
264 // The signal(2) function is the oldest and simplest way to install a signal
265 // handler but it's deprecated.
266 // There are few reasons and most important is that the original Unix
267 // implementation would reset the signal handler to it's default value after
268 // signal is received.
269 ::signal(SIGINT, handler);
270#endif
271}
272
273// Special case for windows. Do not return, wait for the the main thread to
274// return. In any case windows will shut down the application after a timeout
275// of 5 seconds. This wait is required otherwise windows shuts down the process
276// after we return from the signal handler. We could not find better way to
277// handle clean remote shutdown of processes in windows.
278#if defined(_WIN32)
279static void handler_sigbreak(int sig)
280{
281 yarp::os::impl::raise(SIGINT);
282}
283#endif
284
285
287 stopFlag(false),
288 mPriv(nullptr)
289{
291
292 mPriv = new Private(*this);
293
294 //set up signal handlers for catching ctrl-c
295 if (module == nullptr) {
296 module = this;
297 } else {
298 yCInfo(RFMODULE, "RFModule::RFModule() signal handling currently only good for one module.");
299 }
300
301#if defined(_WIN32)
302 yarp::os::impl::signal(SIGBREAK, handler_sigbreak);
303#endif
304
305 yarp::os::impl::signal(SIGINT, handler);
306 yarp::os::impl::signal(SIGTERM, handler);
307}
308
309
311{
312 delete mPriv;
314}
315
316
318{
319 return 1.0;
320}
321
322
324{
325 if (mPriv->getSingletonRunModule()) {
326 return 1;
327 }
328 mPriv->setSingletonRunModule();
329
330 // Setting up main loop
331 // Use yarp::os::Time
332 double currentRun;
333 double elapsed;
334 double sleepPeriod;
335
336 while (!isStopping()) {
338 // If updateModule() returns false we exit the main loop.
339 if (!updateModule()) {
340 break;
341 }
342
343 // At the end of each run of updateModule function, the thread is supposed
344 // to be suspended and release CPU to other threads.
345 // Calling a yield here will help the threads to alternate in the execution.
346 // Note: call yield BEFORE computing elapsed time, so that any time spent due to
347 // yield is took into account and the sleep time is correct.
349
350 // The module is stopped for getPeriod() seconds.
351 // If getPeriod() returns a time > 1 second, we check every second if
352 // the module stopping, and eventually we exit the main loop.
353 do {
356 if (sleepPeriod > 1) {
357 Time::delay(1.0);
358 } else {
360 break;
361 }
362 } while (!isStopping());
363 }
364
365 yCInfo(RFMODULE, "RFModule closing.");
366 if (mPriv->respond_handler->isTerminalAttached()) {
367 yCWarning(RFMODULE, "Module attached to terminal calling exit() to quit.");
368 yCWarning(RFMODULE, "You should be aware that this is not a good way to stop a module. Effects will be:");
369 yCWarning(RFMODULE, "- class destructors will NOT be called");
370 yCWarning(RFMODULE, "- code in the main after runModule() will NOT be executed");
371 yCWarning(RFMODULE, "This happens because in your module you called attachTerminal() and we don't have a portable way to quit a module that is listening to the terminal.");
372 yCWarning(RFMODULE, "At the moment the only way to have the module quit correctly is to avoid listening to terminal (i.e. do not call attachTerminal()).");
373 yCWarning(RFMODULE, "This will also make this annoying message go away.");
374
375 // One day this will hopefully go away, now only way to stop
376 // remove both:
377 close();
378 std::exit(1);
381 }
382
383 close();
384 yCInfo(RFMODULE, "RFModule finished.");
385 return 0;
386}
387
388
390{
391 if (mPriv->getSingletonRunModule()) {
392 return 1;
393 }
394
395 if (!configure(rf)) {
396 yCInfo(RFMODULE, "RFModule failed to open.");
397 return 1;
398 }
399 return runModule();
400}
401
402
404{
405 if (mPriv->getSingletonRunModule()) {
406 return 1;
407 }
408
409 if (!mPriv->newThreadHandler()) {
410 yCError(RFMODULE, "RFModule handling thread failed to allocate.");
411 return 1;
412 }
413
414 if (!mPriv->threaded_handler->start()) {
415 yCError(RFMODULE, "RFModule handling thread failed to start.");
416 return 1;
417 }
418
419 return 0;
420}
421
422
424{
425 if (mPriv->getSingletonRunModule()) {
426 return 1;
427 }
428
429 if (!configure(rf)) {
430 yCError(RFMODULE, "RFModule failed to open.");
431 return 1;
432 }
433
434 return runModuleThreaded();
435}
436
437
439{
440 YARP_UNUSED(rf);
441 return true;
442}
443
444
445bool RFModule::respond(const Bottle& command, Bottle& reply)
446{
447 return basicRespond(command, reply);
448}
449
450
457{
458 mPriv->respond_handler->attach(source);
459 return true;
460}
461
462
464{
465 mPriv->respond_handler->attach(source);
466 return true;
467}
468
469
471{
472 mPriv->respond_handler->attachTerminal();
473 return true;
474}
475
476
478{
479 mPriv->respond_handler->detachTerminal();
480 return true;
481}
482
483
485{
486 return true;
487}
488
489
491{
492 return true;
493}
494
495
496void RFModule::stopModule(bool wait)
497{
498 stopFlag = true;
499
500 if (!interruptModule()) {
501 yCError(RFMODULE, "interruptModule() returned an error there could be problems shutting down the module.");
502 }
503
504 if (wait) {
505 joinModule();
506 }
507}
508
509
511{
512 return stopFlag;
513}
514
515
517{
518 if (mPriv->threaded_handler != nullptr) {
519 if (mPriv->threaded_handler->join(seconds)) {
520 mPriv->deleteThreadHandler();
521 return true;
522 }
523 yCWarning(RFMODULE, "RFModule joinModule() timed out.");
524 return false;
525 }
526 yCWarning(RFMODULE, "Cannot call join: RFModule runModule() is not currently threaded.");
527 return true;
528}
529
530
531std::string RFModule::getName(const std::string& subName)
532{
533 if (subName.empty()) {
534 return name;
535 }
536
537 std::string base = name;
538
539 // Support legacy behavior, check if a "/" needs to be
540 // appended before subName.
541 if (subName[0] != '/') {
542 yCWarning(RFMODULE, "SubName in getName() does not begin with \"/\" this suggest you expect getName() to follow a deprecated behavior.");
543 yCWarning(RFMODULE, "I am now adding \"/\" between %s and %s but you should not rely on this.", name.c_str(), subName.c_str());
544
545 base += "/";
546 }
547
548 base += subName;
549 return base;
550}
551
552
553void RFModule::setName(const char* name)
554{
555 this->name = name;
556}
557
558
559bool RFModule::safeRespond(const Bottle& command, Bottle& reply)
560{
561 bool ok = respond(command, reply);
562 if (!ok) {
563 // just in case derived classes don't correctly pass on messages
564 ok = basicRespond(command, reply);
565 }
566 return ok;
567}
568
569
570bool RFModule::basicRespond(const Bottle& command, Bottle& reply)
571{
572 switch (command.get(0).asVocab32()) {
573 case yarp::os::createVocab32('q', 'u', 'i', 't'):
574 case yarp::os::createVocab32('e', 'x', 'i', 't'):
575 case yarp::os::createVocab32('b', 'y', 'e'):
576 reply.addVocab32("bye");
577 stopModule(false); //calls interruptModule()
578 return true;
579 default:
580 reply.addString("command not recognized");
581 return false;
582 }
583 return false;
584}
static void handler(int sig)
Definition RFModule.cpp:241
bool attach(yarp::os::RpcServer &source)
Definition RFModule.cpp:97
bool attach(yarp::os::Port &source)
Definition RFModule.cpp:89
void run() override
Main body of the new thread.
Definition RFModule.cpp:59
RFModuleRespondHandler(RFModule &owner)
Definition RFModule.cpp:51
bool read(yarp::os::ConnectionReader &connection) override
Handler for reading messages from the network, and passing them on to the respond() method.
Definition RFModule.cpp:129
void run() override
Main body of the new thread.
Definition RFModule.cpp:174
RFModuleThreadedHandler(RFModule &owner)
Definition RFModule.cpp:169
RFModuleThreadedHandler * threaded_handler
Definition RFModule.cpp:189
RFModuleRespondHandler * respond_handler
Definition RFModule.cpp:188
Private(RFModule &owner)
Definition RFModule.cpp:191
void setReader(PortReader &reader) override
Set an external reader for port data.
A simple collection of objects that can be described and transmitted in a portable way.
Definition Bottle.h:64
void add(const Value &value)
Add a Value to the bottle, at the end of the list.
Definition Bottle.cpp:336
size_type size() const
Gets the number of elements in the bottle.
Definition Bottle.cpp:251
bool read(ConnectionReader &reader) override
Set the bottle's value based on input from a network connection.
Definition Bottle.cpp:240
Value & get(size_type index) const
Reads a Value v from a certain part of the list.
Definition Bottle.cpp:246
bool write(ConnectionWriter &writer) const override
Output a representation of the bottle to a network connection.
Definition Bottle.cpp:230
void addString(const char *str)
Places a string in the bottle, at the end of the list.
Definition Bottle.cpp:170
std::string toString() const override
Gives a human-readable textual representation of the bottle.
Definition Bottle.cpp:211
A mini-server for performing network communication in the background.
void write(bool forceStrict=false)
Write the current object being returned by BufferedPort::prepare.
An interface for reading from a network connection.
An interface for writing to a network connection.
virtual bool isTextMode() const =0
Check if the connection is text mode.
static void initMinimum()
Basic system initialization, not including plugins.
Definition Network.cpp:871
static void finiMinimum()
Deinitialization, excluding plugins.
Definition Network.cpp:939
Interface implemented by all objects that can read themselves from the network, such as Bottle object...
Definition PortReader.h:24
A mini-server for network communication.
Definition Port.h:46
void setReader(PortReader &reader) override
Set an external reader for port data.
Definition Port.cpp:511
A base-class for standard YARP modules that supports ResourceFinder.
Definition RFModule.h:20
RFModule()
Constructor.
Definition RFModule.cpp:286
virtual ~RFModule()
Destructor.
Definition RFModule.cpp:310
virtual int runModule()
Calls updateModule() until that returns false.
Definition RFModule.cpp:323
bool attachTerminal()
Make any input from standard input (usually the keyboard) go to the respond() method.
Definition RFModule.cpp:470
virtual bool updateModule()=0
Override this to do whatever your module needs to do.
bool joinModule(double seconds=-1)
The function returns when the thread execution has completed.
Definition RFModule.cpp:516
virtual bool close()
Close function.
Definition RFModule.cpp:490
void setName(const char *name)
Set the name of the module.
Definition RFModule.cpp:553
virtual bool respond(const Bottle &command, Bottle &reply)
Respond to a message.
Definition RFModule.cpp:445
virtual bool attach(yarp::os::Port &source)
Make any input from a Port object go to the respond() method.
Definition RFModule.cpp:456
virtual bool configure(yarp::os::ResourceFinder &rf)
Configure the module, pass a ResourceFinder object to the module.
Definition RFModule.cpp:438
virtual int getThreadKey()
return the Thread unique identifier
Definition RFModule.cpp:233
virtual bool interruptModule()
Try to halt any ongoing operations by threads managed by the module.
Definition RFModule.cpp:484
bool safeRespond(const Bottle &command, Bottle &reply)
Wrapper around respond() that is guaranteed to process system messages.
Definition RFModule.cpp:559
bool detachTerminal()
Detach terminal.
Definition RFModule.cpp:477
void stopModule(bool wait=false)
Ask the module to stop.
Definition RFModule.cpp:496
virtual int runModuleThreaded()
Calls updateModule() on a separate thread until that returns false.
Definition RFModule.cpp:403
bool isStopping()
Check if the module should stop.
Definition RFModule.cpp:510
virtual double getPeriod()
You can override this to control the approximate periodicity at which updateModule() is called by run...
Definition RFModule.cpp:317
std::string getName(const std::string &subName="")
Return name of module, as set with setName().
Definition RFModule.cpp:531
Helper class for finding config files and other external resources.
A port that is specialized as an RPC server.
Definition RpcServer.h:23
An abstraction for a thread of execution.
Definition Thread.h:21
static long int getKeyOfCaller()
Get a unique identifier for the calling thread.
Definition Thread.cpp:130
bool stop()
Stop the thread.
Definition Thread.cpp:81
bool isStopping()
Returns true if the thread is stopping (Thread::stop has been called).
Definition Thread.cpp:99
bool start()
Start the new thread running.
Definition Thread.cpp:93
A single value (typically within a Bottle).
Definition Value.h:43
virtual yarp::conf::vocab32_t asVocab32() const
Get vocabulary identifier as an integer.
Definition Value.cpp:228
virtual bool isList() const
Checks if value is a list.
Definition Value.cpp:162
virtual Bottle * asList() const
Get list value.
Definition Value.cpp:240
std::string toString() const override
Return a standard text representation of the content of the object.
Definition Value.cpp:356
#define yCInfo(component,...)
#define yCError(component,...)
#define yCWarning(component,...)
#define yCDebug(component,...)
#define YARP_OS_LOG_COMPONENT(name, name_string)
void useSystemClock()
Configure YARP to use system time (this is the default).
Definition Time.cpp:137
void yield()
The calling thread releases its remaining quantum upon calling this function.
Definition Time.cpp:131
double now()
Return the current time in seconds, relative to an arbitrary starting point.
Definition Time.cpp:121
void delay(double seconds)
Wait for a certain number of seconds.
Definition Time.cpp:111
std::string readString(bool *eof)
Definition Terminal.cpp:76
The components from which ports and connections are built.
An interface to the operating system, including Port based communication.
constexpr yarp::conf::vocab32_t createVocab32(char a, char b=0, char c=0, char d=0)
Create a vocab from chars.
Definition Vocab.h:27
The main, catch-all namespace for YARP.
Definition dirs.h:16
#define YARP_UNUSED(var)
Definition api.h:162