YARP
Yet Another Robot Platform
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>
19 #include <yarp/os/impl/Terminal.h>
20 
21 #include <cstdio>
22 #include <cstdlib>
23 #include <string>
24 
25 using namespace yarp::os;
26 using namespace yarp::os::impl;
27 
28 namespace {
29 YARP_OS_LOG_COMPONENT(RFMODULE, "yarp.os.RFModule")
30 }
31 
33  public yarp::os::PortReader,
34  public yarp::os::Thread
35 {
36 private:
37  RFModule& owner;
38  bool attachedToPort;
39  bool attachedTerminal;
40 
41 public:
48  bool read(yarp::os::ConnectionReader& connection) override;
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())) {
64  std::string str = yarp::os::impl::Terminal::readString(&isEof);
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 
109  Thread::start();
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 {
165 private:
166  RFModule& owner;
167 
168 public:
170  owner(owner)
171  {
172  }
173 
174  void run() override
175  {
176  owner.runModule();
177  }
178 };
179 
180 
182 {
183 private:
184  RFModule& owner;
185  bool singleton_run_module;
186 
187 public:
190 
191  Private(RFModule& owner) :
192  owner(owner),
193  singleton_run_module(false),
194  respond_handler(nullptr),
195  threaded_handler(nullptr)
196  {
197  respond_handler = new RFModuleRespondHandler(owner);
198  }
199 
201  {
202  delete respond_handler;
203  delete threaded_handler;
204  }
205 
207  {
208  threaded_handler = new RFModuleThreadedHandler(owner);
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 
231 static RFModule* module = nullptr;
232 
234 {
235  if (mPriv->threaded_handler != nullptr) {
236  return mPriv->threaded_handler->getKey();
237  }
239 }
240 
241 static 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)
279 static 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()) {
337  currentRun = yarp::os::Time::now();
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 {
354  elapsed = yarp::os::Time::now() - currentRun;
355  sleepPeriod = getPeriod() - elapsed;
356  if (sleepPeriod > 1) {
357  Time::delay(1.0);
358  } else {
359  Time::delay(sleepPeriod);
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);
380  detachTerminal();
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 
445 bool 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 
496 void 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 
516 bool RFModule::joinModule(double seconds)
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 
531 std::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 
553 void RFModule::setName(const char* name)
554 {
555  this->name = name;
556 }
557 
558 
559 bool 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 
570 bool 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 RFModule * module
Definition: RFModule.cpp:231
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:74
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
An interface for reading from a network connection.
virtual ConnectionWriter * getWriter()=0
Gets a way to reply to the message, if possible.
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:25
A mini-server for network communication.
Definition: Port.h:47
void setReader(PortReader &reader) override
Set an external reader for port data.
Definition: Port.cpp:502
A base-class for standard YARP modules that supports ResourceFinder.
Definition: RFModule.h:21
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:24
An abstraction for a thread of execution.
Definition: Thread.h:22
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 start()
Start the new thread running.
Definition: Thread.cpp:93
A single value (typically within a Bottle).
Definition: Value.h:45
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,...)
Definition: LogComponent.h:132
#define yCError(component,...)
Definition: LogComponent.h:154
#define yCWarning(component,...)
Definition: LogComponent.h:143
#define yCDebug(component,...)
Definition: LogComponent.h:109
#define YARP_OS_LOG_COMPONENT(name, name_string)
Definition: LogComponent.h:34
void useSystemClock()
Configure YARP to use system time (this is the default).
Definition: Time.cpp:144
void yield()
The calling thread releases its remaining quantum upon calling this function.
Definition: Time.cpp:138
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:28
bool read(ImageOf< PixelRgb > &dest, const std::string &src, image_fileformat format=FORMAT_ANY)
Definition: ImageFile.cpp:923
The main, catch-all namespace for YARP.
Definition: dirs.h:16
#define YARP_UNUSED(var)
Definition: api.h:162