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