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