YARP
Yet Another Robot Platform
Robot.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2006-2020 Istituto Italiano di Tecnologia (IIT)
3  * All rights reserved.
4  *
5  * This software may be modified and distributed under the terms of the
6  * BSD-3-Clause license. See the accompanying LICENSE file for details.
7  */
8 
10 
14 
15 #include <yarp/os/LogStream.h>
16 
17 #include <yarp/dev/PolyDriver.h>
19 
20 #include <algorithm>
21 #include <iostream>
22 #include <string>
23 #include <unordered_set>
24 
25 
26 std::ostringstream& operator<<(std::ostringstream& oss, const yarp::robotinterface::experimental::Robot& t)
27 {
28  oss << "(name = \"" << t.name() << "\"";
29  if (!t.params().empty()) {
30  oss << ", params = [";
31  oss << t.params();
32  oss << "]";
33  }
34  if (!t.devices().empty()) {
35  oss << ", devices = [";
36  oss << t.devices();
37  oss << "]";
38  }
39  oss << ")";
40  return oss;
41 }
42 
43 
45 {
46 public:
47  Private(Robot* /*parent*/) :
48  build(0),
50  currentLevel(0)
51  {
52  }
53 
54  // return true if a device with the given name exists
55  bool hasDevice(const std::string& name) const;
56 
57  // return the device with the given name or <fatal error> if not found
58  Device* findDevice(const std::string& name);
59 
60  // return true if a device with the given name exists
61  // considering also the provided external devices
62  bool hasDeviceIncludingExternal(const std::string& name) const;
63 
64  // return the device with the given name or nullptr if not found,
65  // considering also the provided external devices
67 
68  // check if there is no external devices that has the same name of an internal device
69  // return true if there is a conflict, false otherwise
70  bool checkForNamingConflictsInExternalDevices(const yarp::dev::PolyDriverList& newExternalDevicesList);
71 
72  // open all the devices and return true if all the open calls were successful
73  bool openDevices();
74 
75  // close all the devices and return true if all the close calls were successful
76  bool closeDevices();
77 
78  // return a vector of levels that have actions in the requested phase
79  std::vector<unsigned int> getLevels(ActionPhase phase) const;
80 
81  // return a vector of actions for that phase and that level
82  std::vector<std::pair<Device, Action>> getActions(ActionPhase phase, unsigned int level) const;
83 
84 
85  // run configure action on one device
86  bool configure(const Device& device, const ParamList& params);
87 
88  // run calibrate action on one device
89  bool calibrate(const Device& device, const ParamList& params);
90 
91  // run attach action on one device
92  bool attach(const Device& device, const ParamList& params);
93 
94  // run abort action on one device
95  bool abort(const Device& device, const ParamList& params);
96 
97  // run detach action on one device
98  bool detach(const Device& device, const ParamList& params);
99 
100  // run park action on one device
101  bool park(const Device& device, const ParamList& params);
102 
103  // run custom action on one device
104  bool custom(const Device& device, const ParamList& params);
105 
106  std::string name;
107  unsigned int build;
108  std::string portprefix;
113  unsigned int currentLevel;
114 }; // class yarp::robotinterface::experimental::Robot::Private
115 
117 {
118  for (const auto& device : devices) {
119  if (name == device.name()) {
120  return true;
121  }
122  }
123  return false;
124 }
125 
127 {
128  for (auto& device : devices) {
129  if (name == device.name()) {
130  return &device;
131  }
132  }
133  return nullptr;
134 }
135 
137 {
138  if (hasDevice(name)) {
139  return true;
140  } else {
141  for (int i = 0; i < externalDevices.size(); i++) {
142  const yarp::dev::PolyDriverDescriptor* externalDevice = externalDevices[i];
143  if (name == externalDevice->key) {
144  return true;
145  }
146  }
147  }
148  return false;
149 }
150 
153 {
155  deviceFound.poly = nullptr;
156  deviceFound.key = "";
157 
158  for (auto& device : devices) {
159  if (name == device.name()) {
160  deviceFound.poly = device.driver();
161  deviceFound.key = device.name();
162  return deviceFound;
163  }
164  }
165 
166  for (int i = 0; i < externalDevices.size(); i++) {
167  const yarp::dev::PolyDriverDescriptor* externalDevice = externalDevices[i];
168  if (name == externalDevice->key) {
169  deviceFound = *externalDevice;
170  }
171  }
172 
173  return deviceFound;
174 }
175 
177 {
178  std::unordered_set<std::string> externalDevicesNames;
179 
180  for (int i = 0; i < externalDevicesList.size(); i++) {
181  const yarp::dev::PolyDriverDescriptor* externalDevice = externalDevicesList[i];
182  externalDevicesNames.insert(externalDevice->key);
183  }
184 
185  for (auto& device : devices) {
186  if (externalDevicesNames.find(device.name()) != externalDevicesNames.end()) {
187  yError() << "Device name " << device.name() << " is used for both an internal and external device.";
188  return true;
189  }
190  }
191 
192  return false;
193 }
194 
195 
197 {
198  bool ret = true;
199  for (auto& device : devices) {
200  // yDebug() << device;
201 
202  if (!device.open()) {
203  yWarning() << "Cannot open device" << device.name();
204  ret = false;
205  }
206  }
207  if (ret) {
208  // yDebug() << "All devices opened.";
209  } else {
210  yWarning() << "There was some problem opening one or more devices. Please check the log and your configuration";
211  }
212 
213  return ret;
214 }
215 
217 {
218  bool ret = true;
219  for (auto it = devices.rbegin(); it != devices.rend(); ++it) {
221 
222  // yDebug() << device;
223 
224  if (!device.close()) {
225  yWarning() << "Cannot close device" << device.name();
226  ret = false;
227  }
228  }
229  if (ret) {
230  // yDebug() << "All devices closed.";
231  } else {
232  yWarning() << "There was some problem closing one or more devices. Please check the log and your configuration";
233  }
234 
235  return ret;
236 }
237 
239 {
240  std::vector<unsigned int> levels;
241  for (const auto& device : devices) {
242  if (device.actions().empty()) {
243  continue;
244  }
245 
246  for (const auto& action : device.actions()) {
247  if (action.phase() == phase) {
248  levels.push_back(action.level());
249  }
250  }
251  }
252 
253  std::sort(levels.begin(), levels.end());
254  auto it = std::unique(levels.begin(), levels.end());
255  levels.resize(it - levels.begin());
256 
257  return levels;
258 }
259 
260 
261 std::vector<std::pair<yarp::robotinterface::experimental::Device, yarp::robotinterface::experimental::Action>> yarp::robotinterface::experimental::Robot::Private::getActions(yarp::robotinterface::experimental::ActionPhase phase, unsigned int level) const
262 {
263  std::vector<std::pair<yarp::robotinterface::experimental::Device, yarp::robotinterface::experimental::Action>> actions;
264  for (const auto& device : devices) {
265  if (device.actions().empty()) {
266  continue;
267  }
268 
269  for (const auto& action : device.actions()) {
270  if (action.phase() == phase && action.level() == level) {
271  actions.emplace_back(device, action);
272  }
273  }
274  }
275  return actions;
276 }
277 
278 
280 {
281  YARP_FIXME_NOTIMPLEMENTED("configure action")
282  return true;
283 }
284 
287 {
289  yError() << "Action \"" << ActionTypeToString(ActionTypeCalibrate) << R"(" requires "target" parameter)";
290  return false;
291  }
292  std::string targetDeviceName = yarp::robotinterface::experimental::findParam(params, "target");
293 
294  if (!hasDeviceIncludingExternal(targetDeviceName)) {
295  yError() << "Target device" << targetDeviceName << "does not exist.";
296  return false;
297  }
298  yarp::dev::PolyDriverDescriptor targetDevice = findDeviceIncludingExternal(targetDeviceName);
299 
300  return device.calibrate(targetDevice);
301 }
302 
305 {
306  int check = 0;
308  check++;
310  check++;
312  check++;
313 
314  if (check > 1) {
315  yError() << "Action \"" << ActionTypeToString(ActionTypeAttach) << R"(" : you can have only one option: "network" , "networks" or "all" )";
316  return false;
317  }
318 
320 
322  std::string targetNetwork = yarp::robotinterface::experimental::findParam(params, "network");
323 
325  yError() << "Action \"" << ActionTypeToString(ActionTypeAttach) << R"(" requires "device" parameter)";
326  return false;
327  }
328  std::string targetDeviceName = yarp::robotinterface::experimental::findParam(params, "device");
329 
330  if (!hasDeviceIncludingExternal(targetDeviceName)) {
331  yError() << "Target device" << targetDeviceName << "(network =" << targetNetwork << ") does not exist.";
332  return false;
333  }
334  yarp::dev::PolyDriverDescriptor targetDevice = findDeviceIncludingExternal(targetDeviceName);
335 
336  // yDebug() << "Attach device" << device.name() << "to" << targetDevice.name() << "as" << targetNetwork;
337  drivers.push(targetDevice.poly, targetNetwork.c_str());
338 
340  for (auto& device : devices) {
341  drivers.push(device.driver(), "all");
342  }
343 
344  for (int i = 0; i < externalDevices.size(); i++) {
345  const yarp::dev::PolyDriverDescriptor* externalDevice = externalDevices[i];
346  drivers.push(externalDevice->poly, "all");
347  }
348  } else if (yarp::robotinterface::experimental::hasParam(params, "networks")) {
349  yarp::os::Value v;
351  yarp::os::Bottle& targetNetworks = *(v.asList());
352 
353  for (size_t i = 0; i < targetNetworks.size(); ++i) {
354  std::string targetNetwork = targetNetworks.get(i).toString();
355 
357  yError() << "Action \"" << ActionTypeToString(ActionTypeAttach) << "\" requires one parameter per network. \"" << targetNetwork << "\" parameter is missing.";
358  return false;
359  }
360  std::string targetDeviceName = yarp::robotinterface::experimental::findParam(params, targetNetwork);
361  if (!hasDeviceIncludingExternal(targetDeviceName)) {
362  yError() << "Target device" << targetDeviceName << "(network =" << targetNetwork << ") does not exist.";
363  return false;
364  }
365  yarp::dev::PolyDriverDescriptor targetDevice = findDeviceIncludingExternal(targetDeviceName);
366 
367  // yDebug() << "Attach device" << device.name() << "to" << targetDevice.name() << "as" << targetNetwork;
368  drivers.push(targetDevice.poly, targetNetwork.c_str());
369  }
370  } else {
371  yError() << "Action \"" << ActionTypeToString(ActionTypeAttach) << R"(" requires either "network" or "networks" parameter)";
372  return false;
373  }
374 
375  if (!drivers.size()) {
376  yError() << "Action \"" << ActionTypeToString(ActionTypeAttach) << "\" couldn't find any device.";
377  return false;
378  }
379 
380  return device.attach(drivers);
381 }
382 
384 {
385  YARP_FIXME_NOTIMPLEMENTED("abort action")
386  return true;
387 }
388 
389 
391 {
392 
393  if (!params.empty()) {
394  yWarning() << "Action \"" << ActionTypeToString(ActionTypeDetach) << "\" cannot have any parameter. Ignoring them.";
395  }
396 
397  return device.detach();
398 }
399 
402 {
404  yError() << "Action \"" << ActionTypeToString(ActionTypePark) << R"(" requires "target" parameter)";
405  return false;
406  }
407  std::string targetDeviceName = yarp::robotinterface::experimental::findParam(params, "target");
408 
409  if (!hasDeviceIncludingExternal(targetDeviceName)) {
410  yError() << "Target device" << targetDeviceName << "does not exist.";
411  return false;
412  }
413  yarp::dev::PolyDriverDescriptor targetDevice = findDeviceIncludingExternal(targetDeviceName);
414 
415  return device.park(targetDevice);
416 }
417 
419 {
420  YARP_FIXME_NOTIMPLEMENTED("custom action")
421  return true;
422 }
423 
425 {
426  std::ostringstream oss;
427  oss << t;
428  dbg << oss.str();
429  return dbg;
430 }
431 
433  mPriv(new Private(this))
434 {
435 }
436 
438  mPriv(new Private(this))
439 {
440  mPriv->name = name;
441  mPriv->devices = devices;
442 }
443 
445  mPriv(new Private(this))
446 {
447  mPriv->name = other.mPriv->name;
448  mPriv->build = other.mPriv->build;
449  mPriv->portprefix = other.mPriv->portprefix;
450  mPriv->currentPhase = other.mPriv->currentPhase;
451  mPriv->currentLevel = other.mPriv->currentLevel;
452  mPriv->devices = other.mPriv->devices;
453  mPriv->params = other.mPriv->params;
454 }
455 
457 {
458  if (&other != this) {
459  mPriv->name = other.mPriv->name;
460  mPriv->build = other.mPriv->build;
461  mPriv->portprefix = other.mPriv->portprefix;
462  mPriv->currentPhase = other.mPriv->currentPhase;
463  mPriv->currentLevel = other.mPriv->currentLevel;
464 
465  mPriv->devices.clear();
466  mPriv->devices = other.mPriv->devices;
467 
468  mPriv->params.clear();
469  mPriv->params = other.mPriv->params;
470  }
471 
472  return *this;
473 }
474 
476 {
477  delete mPriv;
478 }
479 
481 {
482  return mPriv->name;
483 }
484 
486 {
487  return mPriv->build;
488 }
489 
491 {
492  return mPriv->portprefix;
493 }
494 
496 {
497  for (auto& device : devices()) {
498  ParamList& params = device.params();
499  // Do not override "verbose" param if explicitly set in the xml
500  if (verbose && !yarp::robotinterface::experimental::hasParam(params, "verbose")) {
501  device.params().push_back(Param("verbose", "1"));
502  }
503  }
504 }
505 
507 {
508  for (auto& device : devices()) {
509  ParamList& params = device.params();
510  // Do not override "allow-deprecated-devices" param if explicitly set in the xml
511  if (allowDeprecatedDevices && !yarp::robotinterface::experimental::hasParam(params, "allow-deprecated-devices")) {
512  device.params().push_back(Param("allow-deprecated-devices", "1"));
513  }
514  }
515 }
516 
518 {
519  return mPriv->params;
520 }
521 
523 {
524  return mPriv->devices;
525 }
526 
528 {
529  return *mPriv->findDevice(name);
530 }
531 
533 {
534  return mPriv->name;
535 }
536 
538 {
539  return mPriv->build;
540 }
541 
543 {
544  return mPriv->portprefix;
545 }
546 
548 {
549  return mPriv->params;
550 }
551 
553 {
554  return mPriv->devices;
555 }
556 
558 {
559  return *mPriv->findDevice(name);
560 }
561 
563 {
564  yInfo() << "Interrupt received. Stopping all running threads.";
565 
566  // If we received an interrupt we send a stop signal to all threads
567  // from previous phases
568  for (auto& device : devices()) {
569  device.stopThreads();
570  }
571 }
572 
574 {
575  bool nameConflict = mPriv->checkForNamingConflictsInExternalDevices(list);
576  if (nameConflict) {
577  return false;
578  }
579 
580  mPriv->externalDevices = list;
581  return true;
582 }
583 
585 {
586  yInfo() << ActionPhaseToString(phase) << "phase starting...";
587 
588  mPriv->currentPhase = phase;
589  mPriv->currentLevel = 0;
590 
591  // Open all devices
592  if (phase == ActionPhaseStartup) {
593  if (!mPriv->openDevices()) {
594  yError() << "One or more devices failed opening... see previous log messages for more info";
595  if (!mPriv->closeDevices()) {
596  yError() << "One or more devices failed closing";
597  }
598  return false;
599  }
600  }
601 
602  // run phase does not accept actions
603  if (phase == ActionPhaseRun) {
604  if (mPriv->getLevels(phase).size() != 0) {
605  yWarning() << "Phase" << ActionPhaseToString(phase) << "does not accept actions. Skipping all actions for this phase";
606  }
607  return true;
608  }
609 
610  // Before starting any action we ensure that there are no other
611  // threads running from prevoius phases.
612  // In interrupt 2 and 3 and this is called by the interrupt callback,
613  // and therefore main thread will be blocked and join will never
614  // return. Therefore, since we want to start the abort actions we
615  // skip this check.
616  if (phase != ActionPhaseInterrupt2 && phase != ActionPhaseInterrupt3) {
617  for (auto& device : devices()) {
618  device.joinThreads();
619  }
620  }
621 
622  std::vector<unsigned int> levels = mPriv->getLevels(phase);
623 
624  bool ret = true;
625  for (std::vector<unsigned int>::const_iterator lit = levels.begin(); lit != levels.end(); ++lit) {
626  // for each level
627  const unsigned int level = *lit;
628 
629  yInfo() << "Entering action level" << level << "of phase" << ActionPhaseToString(phase);
630  mPriv->currentLevel = level;
631 
632  // If current phase was changed by some other thread, we should
633  // exit the loop and avoid starting further actions.
634  if (mPriv->currentPhase != phase) {
635  ret = false;
636  break;
637  }
638 
639  std::vector<std::pair<Device, Action>> actions = mPriv->getActions(phase, level);
640 
641  for (auto& ait : actions) {
642  // for each action in that level
643  Device& device = ait.first;
644  Action& action = ait.second;
645 
646  // If current phase was changed by some other thread, we should
647  // exit the loop and avoid starting further actions.
648  if (mPriv->currentPhase != phase) {
649  ret = false;
650  break;
651  }
652 
653  switch (action.type()) {
654  case ActionTypeConfigure:
655  if (!mPriv->configure(device, action.params())) {
656  yError() << "Cannot run configure action on device" << device.name();
657  ret = false;
658  }
659  break;
660  case ActionTypeCalibrate:
661  if (!mPriv->calibrate(device, action.params())) {
662  yError() << "Cannot run calibrate action on device" << device.name();
663  ret = false;
664  }
665  break;
666  case ActionTypeAttach:
667  if (!mPriv->attach(device, action.params())) {
668  yError() << "Cannot run attach action on device" << device.name();
669  ret = false;
670  }
671  break;
672  case ActionTypeAbort:
673  if (!mPriv->abort(device, action.params())) {
674  yError() << "Cannot run abort action on device" << device.name();
675  ret = false;
676  }
677  break;
678  case ActionTypeDetach:
679  if (!mPriv->detach(device, action.params())) {
680  yError() << "Cannot run detach action on device" << device.name();
681  ret = false;
682  }
683  break;
684  case ActionTypePark:
685  if (!mPriv->park(device, action.params())) {
686  yError() << "Cannot run park action on device" << device.name();
687  ret = false;
688  }
689  break;
690  case ActionTypeCustom:
691  if (!mPriv->custom(device, action.params())) {
692  yError() << "Cannot run custom action on device" << device.name();
693  ret = false;
694  }
695  break;
696  default:
697  yWarning() << "Unhandled action" << ActionTypeToString(action.type());
698  ret = false;
699  break;
700  }
701  }
702 
703  yInfo() << "All actions for action level" << level << "of" << ActionPhaseToString(phase) << "phase started. Waiting for unfinished actions.";
704 
705  // Join parallel threads
706  for (auto& device : devices()) {
707  device.joinThreads();
708  // yDebug() << "All actions for device" << device.name() << "at level()" << level << "finished";
709  }
710 
711  yInfo() << "All actions for action level" << level << "of" << ActionPhaseToString(phase) << "phase finished.";
712  }
713 
714  if (!ret) {
715  yWarning() << "There was some problem running actions for" << ActionPhaseToString(phase) << "phase . Please check the log and your configuration";
716  }
717 
718  if (phase == ActionPhaseShutdown) {
719  if (!mPriv->closeDevices()) {
720  yError() << "One or more devices failed closing";
721  return false;
722  }
723  }
724 
725  yInfo() << ActionPhaseToString(phase) << "phase finished.";
726 
727  return ret;
728 }
729 
731 {
732  return mPriv->currentPhase;
733 }
734 
736 {
737  return mPriv->currentLevel;
738 }
739 
740 bool yarp::robotinterface::experimental::Robot::hasParam(const std::string& name) const
741 {
742  return yarp::robotinterface::experimental::hasParam(mPriv->params, name);
743 }
744 
745 std::string yarp::robotinterface::experimental::Robot::findParam(const std::string& name) const
746 {
747  return yarp::robotinterface::experimental::findParam(mPriv->params, name);
748 }
float t
bool ret
#define yInfo(...)
Definition: Log.h:260
#define yError(...)
Definition: Log.h:282
#define YARP_FIXME_NOTIMPLEMENTED(what)
Definition: Log.h:310
#define yWarning(...)
Definition: Log.h:271
std::ostringstream & operator<<(std::ostringstream &oss, const yarp::robotinterface::experimental::Robot &t)
Definition: Robot.cpp:26
void push(PolyDriver *p, const char *k)
A simple collection of objects that can be described and transmitted in a portable way.
Definition: Bottle.h:73
size_type size() const
Gets the number of elements in the bottle.
Definition: Bottle.cpp:254
Value & get(size_type index) const
Reads a Value v from a certain part of the list.
Definition: Bottle.cpp:249
A single value (typically within a Bottle).
Definition: Value.h:47
virtual Bottle * asList() const
Get list value.
Definition: Value.cpp:243
std::string toString() const override
Return a standard text representation of the content of the object.
Definition: Value.cpp:359
void fromString(const char *str)
Set value to correspond to a textual representation.
Definition: Value.cpp:354
bool attach(const yarp::dev::PolyDriverList &drivers) const
Definition: Device.cpp:425
yarp::dev::PolyDriver * driver() const
Definition: Device.cpp:346
bool calibrate(const yarp::dev::PolyDriverDescriptor &target) const
Definition: Device.cpp:366
bool park(const yarp::dev::PolyDriverDescriptor &target) const
Definition: Device.cpp:498
bool custom(const Device &device, const ParamList &params)
Definition: Robot.cpp:418
bool detach(const Device &device, const ParamList &params)
Definition: Robot.cpp:390
bool attach(const Device &device, const ParamList &params)
Definition: Robot.cpp:303
bool abort(const Device &device, const ParamList &params)
Definition: Robot.cpp:383
yarp::robotinterface::experimental::ActionPhase currentPhase
Definition: Robot.cpp:112
bool hasDeviceIncludingExternal(const std::string &name) const
Definition: Robot.cpp:136
bool checkForNamingConflictsInExternalDevices(const yarp::dev::PolyDriverList &newExternalDevicesList)
Definition: Robot.cpp:176
std::vector< unsigned int > getLevels(ActionPhase phase) const
Definition: Robot.cpp:238
Device * findDevice(const std::string &name)
Definition: Robot.cpp:126
bool hasDevice(const std::string &name) const
Definition: Robot.cpp:116
std::vector< std::pair< Device, Action > > getActions(ActionPhase phase, unsigned int level) const
Definition: Robot.cpp:261
yarp::dev::PolyDriverDescriptor findDeviceIncludingExternal(const std::string &name)
Definition: Robot.cpp:152
bool park(const Device &device, const ParamList &params)
Definition: Robot.cpp:400
bool calibrate(const Device &device, const ParamList &params)
Definition: Robot.cpp:285
bool configure(const Device &device, const ParamList &params)
Definition: Robot.cpp:279
bool hasParam(const std::string &name) const
Definition: Robot.cpp:740
void setAllowDeprecatedDevices(bool allowDeprecatedDevices)
Definition: Robot.cpp:506
bool enterPhase(yarp::robotinterface::experimental::ActionPhase phase)
Definition: Robot.cpp:584
std::string findParam(const std::string &name) const
Definition: Robot.cpp:745
Device & device(const std::string &name)
Definition: Robot.cpp:527
Robot & operator=(const Robot &other)
Definition: Robot.cpp:456
yarp::robotinterface::experimental::ActionPhase currentPhase() const
Definition: Robot.cpp:730
bool setExternalDevices(const yarp::dev::PolyDriverList &list)
Definition: Robot.cpp:573
std::string findParam(const robotinterface::experimental::ParamList &list, const std::string &name)
Definition: Types.cpp:29
bool hasParam(const robotinterface::experimental::ParamList &list, const std::string &name)
Definition: Types.cpp:19
std::string ActionPhaseToString(robotinterface::experimental::ActionPhase actionphase)
Definition: Types.cpp:101
std::vector< robotinterface::experimental::Device > DeviceList
Definition: Types.h:36
std::string ActionTypeToString(robotinterface::experimental::ActionType actiontype)
Definition: Types.cpp:149
std::vector< robotinterface::experimental::Param > ParamList
Definition: Types.h:32