YARP
Yet Another Robot Platform
ControlBoardWrapper.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 "ControlBoardWrapper.h"
8 
9 #include <yarp/os/LogComponent.h>
10 #include <yarp/os/LogStream.h>
11 
13 
15 #include "RPCMessagesParser.h"
17 #include <algorithm>
18 #include <cstring> // for memset function
19 #include <iostream>
20 #include <numeric>
21 #include <sstream>
22 
23 using namespace yarp::os;
24 using namespace yarp::dev;
25 using namespace yarp::dev::impl;
26 using namespace yarp::sig;
27 
28 
30  yarp::os::PeriodicThread(default_period)
31 {
32  streaming_parser.init(this);
33  RPC_parser.init(this);
34 }
35 
36 void ControlBoardWrapper::cleanup_yarpPorts()
37 {
38  //shut down control port
39  inputRPCPort.interrupt();
40  inputRPCPort.removeCallbackLock();
41  inputRPCPort.close();
42 
43  inputStreamingPort.interrupt();
44  inputStreamingPort.close();
45 
46  outputPositionStatePort.interrupt();
47  outputPositionStatePort.close();
48 
49  extendedOutputStatePort.interrupt();
50  extendedOutputStatePort.close();
51 
52  rpcData.destroy();
53 }
54 
56 
58 {
59  //stop thread if running
60  detachAll();
61 
64  }
65 
66  if (useROS != ROS_only) {
67  cleanup_yarpPorts();
68  }
69 
70  if (rosNode != nullptr) {
71  delete rosNode;
72  rosNode = nullptr;
73  }
74 
75  //if we own a deviced we have to close and delete it
76  if (ownDevices) {
77  // we should have created a new devices which we need to delete
78  if (subDeviceOwned != nullptr) {
79  subDeviceOwned->close();
80  delete subDeviceOwned;
81  subDeviceOwned = nullptr;
82  }
83  } else {
84  detachAll();
85  }
86  return true;
87 }
88 
89 bool ControlBoardWrapper::checkPortName(Searchable& params)
90 {
91  /* see if rootName is present in the config file, this param is not used from long time, so it'll be
92  * marked as deprecated.
93  */
94  if (params.check("rootName")) {
96  "************************************************************************************\n"
97  "* controlboardwrapper2 is using the deprecated parameter 'rootName' for port name, *\n"
98  "* It has to be removed and substituted with: *\n"
99  "* name: full port prefix name with leading '/', e.g. /robotName/part/ *\n"
100  "************************************************************************************";
101  rootName = params.find("rootName").asString();
102  }
103 
104  // find name as port name (similar both in new and old policy
105  if (!params.check("name")) {
107  "************************************************************************************\n"
108  "* controlboardwrapper2 missing mandatory parameter 'name' for port name, usage is: *\n"
109  "* name: full port prefix name with leading '/', e.g. /robotName/part/ *\n"
110  "************************************************************************************";
111  return false;
112  }
113 
114  partName = params.find("name").asString();
115  if (partName[0] != '/') {
117  "************************************************************************************\n"
118  "* controlboardwrapper2 'name' parameter for port name does not follow convention, *\n"
119  "* it MUST start with a leading '/' since it is used as the full prefix port name *\n"
120  "* name: full port prefix name with leading '/', e.g. /robotName/part/ *\n"
121  "* A temporary automatic fix will be done for you, but please fix your config file *\n"
122  "************************************************************************************";
123  rootName = "/" + partName;
124  } else {
125  rootName = partName;
126  }
127 
128  return true;
129 }
130 
131 bool ControlBoardWrapper::checkROSParams(Searchable& config)
132 {
133  // check for ROS parameter group
134  if (!config.check("ROS")) {
135  useROS = ROS_disabled;
136  return true;
137  }
138 
139  yCInfo(CONTROLBOARD) << "ROS group was FOUND in config file.";
140 
141  Bottle& rosGroup = config.findGroup("ROS");
142  if (rosGroup.isNull()) {
143  yCError(CONTROLBOARD) << partName << "ROS group params is not a valid group or empty";
144  useROS = ROS_config_error;
145  return false;
146  }
147 
148  // check for useROS parameter
149  if (!rosGroup.check("useROS")) {
150  yCError(CONTROLBOARD) << partName << " cannot find useROS parameter, mandatory when using ROS message. \n \
151  Allowed values are true, false, ROS_only";
152  useROS = ROS_config_error;
153  return false;
154  }
155  std::string ros_use_type = rosGroup.find("useROS").asString();
156  if (ros_use_type == "false") {
157  yCInfo(CONTROLBOARD) << partName << "useROS topic if set to 'false'";
158  useROS = ROS_disabled;
159  return true;
160  }
161 
162  if (ros_use_type == "true") {
163  yCInfo(CONTROLBOARD) << partName << "useROS topic if set to 'true'";
164  useROS = ROS_enabled;
165  } else if (ros_use_type == "only") {
166  yCInfo(CONTROLBOARD) << partName << "useROS topic if set to 'only";
167  useROS = ROS_only;
168  } else {
169  yCInfo(CONTROLBOARD) << partName << "useROS parameter is seet to unvalid value ('" << ros_use_type << "'), supported values are 'true', 'false', 'only'";
170  useROS = ROS_config_error;
171  return false;
172  }
173 
174  // check for ROS_nodeName parameter
175  if (!rosGroup.check("ROS_nodeName")) {
176  yCError(CONTROLBOARD) << partName << " cannot find ROS_nodeName parameter, mandatory when using ROS message";
177  useROS = ROS_config_error;
178  return false;
179  }
180  rosNodeName = rosGroup.find("ROS_nodeName").asString();
181  if(rosNodeName[0] != '/'){
182  yCError(CONTROLBOARD) << "ros node name must begin with an initial /";
183  return false;
184  }
185  yCInfo(CONTROLBOARD) << partName << "rosNodeName is " << rosNodeName;
186 
187  // check for ROS_topicName parameter
188  if (!rosGroup.check("ROS_topicName")) {
189  yCError(CONTROLBOARD) << partName << " cannot find rosTopicName parameter, mandatory when using ROS message";
190  useROS = ROS_config_error;
191  return false;
192  }
193  rosTopicName = rosGroup.find("ROS_topicName").asString();
194  yCInfo(CONTROLBOARD) << partName << "rosTopicName is " << rosTopicName;
195 
196  // check for rosNodeName parameter
197  // UPDATE: joint names are got from MotionControl subdevice now.
198  // An error should be thrown later on in case we fail getting names from device
199  if (!rosGroup.check("jointNames")) {
200  yCInfo(CONTROLBOARD) << partName << "ROS topic has been required, jointNames will be got from motionControl subdevice.";
201  } else // if names are there, store them. They will be used for back compatibility if old policy is used.
202  {
203  Bottle nameList = rosGroup.findGroup("jointNames").tail();
204  if (nameList.isNull()) {
205  yCError(CONTROLBOARD) << partName << " jointNames not found";
206  useROS = ROS_config_error;
207  return false;
208  }
209 
210  if (nameList.size() != static_cast<size_t>(controlledJoints)) {
211  yCError(CONTROLBOARD) << partName << " jointNames incorrect number of entries. \n jointNames is " << nameList.toString() << "while expected length is " << controlledJoints;
212  useROS = ROS_config_error;
213  return false;
214  }
215 
216  jointNames.clear();
217  for (size_t i = 0; i < controlledJoints; i++) {
218  jointNames.push_back(nameList.get(i).toString());
219  }
220  }
221  return true;
222 }
223 
224 bool ControlBoardWrapper::initialize_ROS()
225 {
226  bool success = false;
227  switch (useROS) {
228  case ROS_enabled:
229  case ROS_only: {
230  rosNode = new yarp::os::Node(rosNodeName); // add a ROS node
231 
232  if (rosNode == nullptr) {
233  yCError(CONTROLBOARD) << " opening " << rosNodeName << " Node, check your yarp-ROS network configuration";
234  success = false;
235  break;
236  }
237 
238  if (!rosPublisherPort.topic(rosTopicName)) {
239  yCError(CONTROLBOARD) << " opening " << rosTopicName << " Topic, check your yarp-ROS network configuration";
240  success = false;
241  break;
242  }
243  success = true;
244  } break;
245 
246  case ROS_disabled: {
247  yCInfo(CONTROLBOARD) << partName << ": no ROS initialization required";
248  success = true;
249  } break;
250 
251  case ROS_config_error: {
252  yCError(CONTROLBOARD) << partName << " ROS parameter are not correct, check your configuration file";
253  success = false;
254  } break;
255 
256  default:
257  {
258  yCError(CONTROLBOARD) << partName << " something went wrong with ROS configuration, we should never be here!!!";
259  success = false;
260  } break;
261  }
262  return success;
263 }
264 
265 bool ControlBoardWrapper::initialize_YARP(yarp::os::Searchable& prop)
266 {
267  bool success = false;
268 
269  switch (useROS) {
270  case ROS_only: {
271  yCInfo(CONTROLBOARD) << partName << " No YARP initialization required";
272  success = true;
273  } break;
274 
275  default:
276  {
277  yCInfo(CONTROLBOARD) << partName << " initting YARP initialization";
278  // initialize callback
279  if (!streaming_parser.initialize()) {
280  yCError(CONTROLBOARD) << "Error could not initialize callback object";
281  success = false;
282  break;
283  }
284 
285  rootName = prop.check("rootName", Value("/"), "starting '/' if needed.").asString();
286  partName = prop.check("name", Value("controlboard"), "prefix for port names").asString();
287  rootName += (partName);
288  if (rootName.find("//") != std::string::npos) {
289  rootName.replace(rootName.find("//"), 2, "/");
290  }
291 
293  if (!inputRPCPort.open((rootName + "/rpc:i"))) {
294  yCError(CONTROLBOARD) << "Error opening port " << rootName + "/rpc:i";
295  success = false;
296  break;
297  }
298  inputRPCPort.setReader(RPC_parser);
299  inputRPC_buffer.attach(inputRPCPort);
300  RPC_parser.attach(inputRPC_buffer);
301 
302  if (!inputStreamingPort.open(rootName + "/command:i")) {
303  yCError(CONTROLBOARD) << "Error opening port " << rootName + "/rpc:i";
304  success = false;
305  break;
306  }
307 
308  // attach callback.
309  inputStreamingPort.setStrict();
310  inputStreamingPort.useCallback(streaming_parser);
311 
312  if (!outputPositionStatePort.open(rootName + "/state:o")) {
313  yCError(CONTROLBOARD) << "Error opening port " << rootName + "/state:o";
314  success = false;
315  break;
316  }
317 
318  // new extended output state port
319  if (!extendedOutputStatePort.open(rootName + "/stateExt:o")) {
320  yCError(CONTROLBOARD) << "Error opening port " << rootName + "/state:o";
321  success = false;
322  break;
323  }
324  extendedOutputState_buffer.attach(extendedOutputStatePort);
325  success = true;
326  } break;
327  } // end switch
328 
329  // cleanup if something went wrong
330  if (!success) {
331  cleanup_yarpPorts();
332  }
333  return success;
334 }
335 
336 
338 {
339  yCWarning(CONTROLBOARD) << "The 'controlboardwrapper2' device is deprecated in favour of 'controlboardremapper' + 'controlBoard_nws_yarp'.";
340  yCWarning(CONTROLBOARD) << "The old device is no longer supported, and it will be deprecated in YARP 3.6 and removed in YARP 4.";
341  yCWarning(CONTROLBOARD) << "Please update your scripts.";
342 
343  Property prop;
344  prop.fromString(config.toString());
345 
346  if (prop.check("verbose", "Deprecated flag. Use log components instead")) {
347  yCWarning(CONTROLBOARD) << "'verbose' flag is deprecated. Use log components instead";
348  }
349 
350  if (!checkPortName(config)) {
351  yCError(CONTROLBOARD) << "'portName' was not correctly set, check you r configuration file";
352  return false;
353  }
354 
355  // check FIRST for deprecated parameter
356  if (prop.check("threadrate")) {
357  yCError(CONTROLBOARD) << "Using removed parameter 'threadrate', use 'period' instead";
358  return false;
359  }
360 
361  // NOW, check for correct parameter, so if both are present we use the correct one
362  if (prop.check("period")) {
363  if (!prop.find("period").isInt32()) {
364  yCError(CONTROLBOARD) << "'period' parameter is not an integer value";
365  return false;
366  }
367  period = prop.find("period").asInt32() / 1000.0;
368  if (period <= 0) {
369  yCError(CONTROLBOARD) << "'period' parameter is not valid, read value is" << period;
370  return false;
371  }
372  } else {
373  yCDebug(CONTROLBOARD) << "'period' parameter missing, using default thread period = 20ms";
374  period = default_period;
375  }
376 
377  // check if we need to create subdevice or if they are
378  // passed later on thorugh attachAll()
379  if (prop.check("subdevice")) {
380  ownDevices = true;
381  prop.setMonitor(config.getMonitor());
382  if (!openAndAttachSubDevice(prop)) {
383  yCError(CONTROLBOARD, "Error while opening subdevice");
384  return false;
385  }
386  } else {
387  ownDevices = false;
388  if (!openDeferredAttach(prop)) {
389  return false;
390  }
391  }
392 
393  // using controlledJoints here will allocate more memory than required, but not so much.
395 
396  /* This must be after the openAndAttachSubDevice() or openDeferredAttach() in order to have the correct number of controlledJoints,
397  but before the initialize_ROS and initialize_YARP */
398  if (!checkROSParams(config)) {
399  yCError(CONTROLBOARD) << partName << " ROS parameter are not correct, check your configuration file";
400  return false;
401  }
402 
403  // call ROS node/topic initialization, if needed
404  if (!initialize_ROS()) {
405  return false;
406  }
407 
408  // call YARP port initialization, if needed
409  if (!initialize_YARP(prop)) {
410  yCError(CONTROLBOARD) << partName << "Something wrong when initting yarp ports";
411  return false;
412  }
413 
414  times.resize(controlledJoints);
415  ros_struct.name.resize(controlledJoints);
416  ros_struct.position.resize(controlledJoints);
417  ros_struct.velocity.resize(controlledJoints);
418  ros_struct.effort.resize(controlledJoints);
419 
420  // In case attach is not deferred and the controlboard already owns a valid device
421  // we can start the thread. Otherwise this will happen when attachAll is called
422  if (ownDevices) {
423  PeriodicThread::setPeriod(period);
424  if (!PeriodicThread::start()) {
425  return false;
426  }
427  }
428  return true;
429 }
430 
431 
432 // Default usage
433 // Open the wrapper only, the attach method needs to be called before using it
434 bool ControlBoardWrapper::openDeferredAttach(Property& prop)
435 {
436  if (!prop.check("networks", "list of networks merged by this wrapper")) {
437  yCError(CONTROLBOARD) << "List of networks to attach to was not found.";
438  return false;
439  }
440 
441  Bottle* nets = prop.find("networks").asList();
442  if (nets == nullptr) {
443  yCError(CONTROLBOARD) << "Error parsing parameters: \"networks\" should be followed by a list";
444  return false;
445  }
446 
447  if (!prop.check("joints", "number of joints of the part")) {
448  return false;
449  }
450 
451  controlledJoints = prop.find("joints").asInt32();
452 
453  size_t nsubdevices = nets->size();
454  device.lut.resize(controlledJoints);
455  device.subdevices.resize(nsubdevices);
456 
457  // configure the devices
458  size_t totalJ = 0;
459  for (size_t k = 0; k < nets->size(); k++) {
460  Bottle parameters;
461  size_t wBase;
462  size_t wTop;
463  size_t base;
464  size_t top;
465 
466  parameters = prop.findGroup(nets->get(k).asString());
467 
468  if (parameters.size() == 2) {
469  Bottle* bot = parameters.get(1).asList();
470  Bottle tmpBot;
471  if (bot == nullptr) {
472  // probably data are not passed in the correct way, try to read them as a string.
473  std::string bString(parameters.get(1).asString());
474  tmpBot.fromString(bString);
475 
476  if (tmpBot.size() != 4) {
477  yCError(CONTROLBOARD) << "Error: check network parameters in part description"
478  << "--> I was expecting " << nets->get(k).asString() << " followed by a list of four integers in parenthesis"
479  << "Got: " << parameters.toString();
480  return false;
481  }
482 
483  bot = &tmpBot;
484  }
485 
486  // If I came here, bot is correct
487  wBase = static_cast<size_t>(bot->get(0).asInt32());
488  wTop = static_cast<size_t>(bot->get(1).asInt32());
489  base = static_cast<size_t>(bot->get(2).asInt32());
490  top = static_cast<size_t>(bot->get(3).asInt32());
491  } else if (parameters.size() == 5) {
492  yCError(CONTROLBOARD) << "Parameter networks use deprecated syntax";
493  wBase = static_cast<size_t>(parameters.get(1).asInt32());
494  wTop = static_cast<size_t>(parameters.get(2).asInt32());
495  base = static_cast<size_t>(parameters.get(3).asInt32());
496  top = static_cast<size_t>(parameters.get(4).asInt32());
497  } else {
498  yCError(CONTROLBOARD) << "Error: check network parameters in part description"
499  << "--> I was expecting " << nets->get(k).asString() << " followed by a list of four integers in parenthesis"
500  << "Got: " << parameters.toString();
501  return false;
502  }
503 
504  SubDevice* tmpDevice = device.getSubdevice(k);
505  if (!tmpDevice) {
506  yCError(CONTROLBOARD) << "Get of subdevice returned null";
507  return false;
508  }
509 
510  size_t axes = top - base + 1;
511  if (!tmpDevice->configure(wBase, wTop, base, top, axes, nets->get(k).asString(), getId())) {
512  yCError(CONTROLBOARD) << "Configure of subdevice ret false";
513  return false;
514  }
515 
516  // Check input values are in range
517  if ((wBase == static_cast<size_t>(-1)) || (wBase >= controlledJoints)) {
518  yCError(CONTROLBOARD) << "Input configuration for device " << partName << "has a wrong attach map."
519  << "First index " << wBase << "must be inside range from 0 to 'joints' (" << controlledJoints << ")";
520  return false;
521  }
522 
523  if ((wTop == static_cast<size_t>(-1)) || (wTop >= controlledJoints)) {
524  yCError(CONTROLBOARD) << "Input configuration for device " << partName << "has a wrong attach map."
525  << "Second index " << wTop << "must be inside range from 0 to 'joints' (" << controlledJoints << ")";
526  return false;
527  }
528 
529  if (wBase > wTop) {
530  yCError(CONTROLBOARD) << "Input configuration for device " << partName << "has a wrong attach map."
531  << "First index " << wBase << "must be lower than second index " << wTop;
532  return false;
533  }
534 
535  for (size_t j = wBase, jInDev = base; j <= wTop; j++, jInDev++) {
536  device.lut[j].deviceEntry = k;
537  device.lut[j].offset = static_cast<int>(j - wBase);
538  device.lut[j].jointIndexInDev = jInDev;
539  }
540 
541  totalJ += axes;
542  }
543 
544  if (totalJ != controlledJoints) {
545  yCError(CONTROLBOARD) << "Error total number of mapped joints (" << totalJ << ") does not correspond to part joints (" << controlledJoints << ")";
546  return false;
547  }
548  return true;
549 }
550 
551 // For the simulator, if a subdevice parameter is given to the wrapper, it will
552 // open it and attach to immediately.
553 bool ControlBoardWrapper::openAndAttachSubDevice(Property& prop)
554 {
555  Property p;
556  subDeviceOwned = new PolyDriver;
557  p.fromString(prop.toString());
558 
559  std::string subdevice = prop.find("subdevice").asString();
560  p.setMonitor(prop.getMonitor(), subdevice.c_str()); // pass on any monitoring
561  p.unput("device");
562  p.put("device", subdevice); // subdevice was already checked before
563 
564  // if errors occurred during open, quit here.
565  yCDebug(CONTROLBOARD, "opening subdevice");
566  subDeviceOwned->open(p);
567 
568  if (!subDeviceOwned->isValid()) {
569  yCError(CONTROLBOARD, "opening subdevice... FAILED");
570  return false;
571  }
572 
573  yarp::dev::IEncoders* iencs = nullptr;
574 
575  subDeviceOwned->view(iencs);
576 
577  if (iencs == nullptr) {
578  yCError(CONTROLBOARD, "Opening IEncoders interface of subdevice... FAILED");
579  return false;
580  }
581 
582  int tmp_axes;
583  bool getAx = iencs->getAxes(&tmp_axes);
584 
585  if (!getAx) {
586  yCError(CONTROLBOARD, "Calling getAxes of subdevice... FAILED");
587  return false;
588  }
589  controlledJoints = static_cast<size_t>(tmp_axes);
590 
591  yCDebug(CONTROLBOARD, "Joints parameter is %zu", controlledJoints);
592 
593 
594  device.lut.resize(controlledJoints);
595  device.subdevices.resize(1);
596 
597  // configure the device
598  size_t base = 0;
599  size_t top = controlledJoints - 1;
600 
601  size_t wbase = base;
602  size_t wtop = top;
603  SubDevice* tmpDevice = device.getSubdevice(0);
604 
605  std::string subDevName((partName + "_" + subdevice));
606  if (!tmpDevice->configure(wbase, wtop, base, top, controlledJoints, subDevName, getId())) {
607  yCError(CONTROLBOARD) << "Configure of subdevice ret false";
608  return false;
609  }
610 
611  for (size_t j = 0; j < controlledJoints; j++) {
612  device.lut[j].deviceEntry = 0;
613  device.lut[j].offset = j;
614  }
615 
616 
617  if (!device.subdevices[0].attach(subDeviceOwned, subDevName)) {
618  return false;
619  }
620 
621  // initialization.
622  RPC_parser.initialize();
623  updateAxisName();
624  calculateMaxNumOfJointsInDevices();
625  return true;
626 }
627 
628 void ControlBoardWrapper::calculateMaxNumOfJointsInDevices()
629 {
631 
632  for (size_t d = 0; d < device.subdevices.size(); d++) {
633  SubDevice* p = device.getSubdevice(d);
636  }
637  }
638 }
639 
640 bool ControlBoardWrapper::updateAxisName()
641 {
642  // If attached device has axisName update the internal values, otherwise keep the on from wrapper
643  // config file, if any.
644  // IMPORTANT!! This function has to be called BEFORE the thread starts, because if ROS is enabled,
645  // the name has to be correct right from the first message!!
646 
647  // FOR THE FUTURE: this double version will be dropped because it'll create confusion. Only the names
648  // from the motionControl device will be considered good
649 
650  // no need to update this variable if we are not using ROS. YARP RPC will always call the sudevice.
651  if (useROS == ROS_disabled) {
652  return true;
653  }
654 
655  std::string tmp;
656  // I need a temporary vector because if I'm wrapping more than one subdevice, and one of them
657  // does not have the axesName, then I'd stick with the old names from wrpper config file, if any.
658  std::vector<std::string> tmpVect;
659  bool ret = true;
660 
661  tmpVect.clear();
662  for (size_t i = 0; i < controlledJoints; i++) {
663  if ((ret = getAxisName(i, tmp) && ret)) {
664  std::string tmp2(tmp);
665  tmpVect.push_back(tmp2);
666  }
667  }
668 
669  if (ret) {
670  if (!jointNames.empty()) {
671  yCWarning(CONTROLBOARD) << "Found 2 instance of jointNames parameter: one in the wrapper [ROS] group and another one in the subdevice, the latter one will be used.";
672  std::string fullNames;
673  for (size_t i = 0; i < controlledJoints; i++) {
674  fullNames.append(tmpVect[i]);
675  }
676  }
677 
678  jointNames.clear();
679  jointNames = tmpVect;
680  } else {
681  if (jointNames.empty()) {
682  yCError(CONTROLBOARD) << "Joint names were not found! they are mandatory when using ROS topic";
683  return false;
684  }
685 
686  yCWarning(CONTROLBOARD) << "\n" <<
687  "************************************************************************************************** \n" <<
688  "* Joint names for ROS topic were found in the [ROS] group in the wrapper config file for\n" <<
689  "* '" << partName << "' device.\n" <<
690  "* They should be in the MotionControl device(s) instead. Please update the config files.\n" <<
691  "**************************************************************************************************";
692  }
693  return true;
694 }
695 
696 
698 {
699  //check if we already instantiated a subdevice previously
700  if (ownDevices) {
701  return false;
702  }
703 
704  for (int p = 0; p < polylist.size(); p++) {
705  // look if we have to attach to a calibrator
706  std::string tmpKey = polylist[p]->key;
707  if (tmpKey == "Calibrator" || tmpKey == "calibrator") {
708  // Set the IRemoteCalibrator interface, the wrapper must point to the calibrator device
709  yarp::dev::IRemoteCalibrator* calibrator;
710  polylist[p]->poly->view(calibrator);
711 
712  IRemoteCalibrator::setCalibratorDevice(calibrator);
713  continue;
714  }
715 
716  // find appropriate entry in list of subdevices and attach
717  size_t k = 0;
718  for (k = 0; k < device.subdevices.size(); k++) {
719  if (device.subdevices[k].id == tmpKey) {
720  if (!device.subdevices[k].attach(polylist[p]->poly, tmpKey)) {
721  yCError(CONTROLBOARD, "Attach to subdevice %s failed", polylist[p]->key.c_str());
722  return false;
723  }
724  }
725  }
726  }
727 
728  //check if all devices are attached to the driver
729  bool ready = true;
730  for (auto& subdevice : device.subdevices) {
731  if (!subdevice.isAttached()) {
732  yCError(CONTROLBOARD, "Device %s was not found in the list passed to attachAll", subdevice.id.c_str());
733  ready = false;
734  }
735  }
736 
737  if (!ready) {
738  yCError(CONTROLBOARD, "AttachAll failed, some subdevice was not found or its attach failed");
739  std::stringstream ss;
740  for (int p = 0; p < polylist.size(); p++) {
741  ss << polylist[p]->key.c_str() << " ";
742  }
743  yCError(CONTROLBOARD, "List of devices keys passed to attachAll: %s", ss.str().c_str());
744  return false;
745  }
746 
747  // initialization.
748  RPC_parser.initialize();
749 
750  updateAxisName();
751  calculateMaxNumOfJointsInDevices();
752  PeriodicThread::setPeriod(period);
753  return PeriodicThread::start();
754 }
755 
757 {
758  //check if we already instantiated a subdevice previously
759  if (ownDevices) {
760  return false;
761  }
762 
765  }
766 
767  int devices = device.subdevices.size();
768 
769  for (int k = 0; k < devices; k++) {
770  SubDevice* sub = device.getSubdevice(k);
771  if (sub) {
772  sub->detach();
773  }
774  }
775 
776  IRemoteCalibrator::releaseCalibratorDevice();
777  return true;
778 }
779 
781 {
782  // check we are not overflowing with input messages
783  if (inputStreamingPort.getPendingReads() >= 20) {
784  yCWarning(CONTROLBOARD) << "Number of streaming input messages to be read is " << inputStreamingPort.getPendingReads() << " and can overflow";
785  }
786 
787  // Small optimization: Avoid to call getEncoders twice, one for YARP port
788  // and again for ROS topic.
789  //
790  // Calling getStuff here on ros_struct because it is a class member, hence
791  // always available. In the other side, to have the yarp struct to write into
792  // it will be rewuired to call port.prepare, that it is something I should
793  // not do if the wrapper is in ROS_only configuration.
794 
795  bool positionsOk = getEncodersTimed(ros_struct.position.data(), times.data());
796  bool speedsOk = getEncoderSpeeds(ros_struct.velocity.data());
797  bool torqueOk = getTorques(ros_struct.effort.data());
798 
799  // Update the port envelope time by averaging all timestamps
800  timeMutex.lock();
801  time.update(std::accumulate(times.begin(), times.end(), 0.0) / controlledJoints);
802  yarp::os::Stamp averageTime = time;
803  timeMutex.unlock();
804 
805  if (useROS != ROS_only) {
806  // handle stateExt first
807  jointData& yarp_struct = extendedOutputState_buffer.get();
808 
815  yarp_struct.torque.resize(controlledJoints);
816  yarp_struct.pwmDutycycle.resize(controlledJoints);
817  yarp_struct.current.resize(controlledJoints);
818  yarp_struct.controlMode.resize(controlledJoints);
820 
821  // Get already stored data from before. This is to avoid a double call to HW device,
822  // which may require more time. //
823  yarp_struct.jointPosition_isValid = positionsOk;
824  std::copy(ros_struct.position.begin(), ros_struct.position.end(), yarp_struct.jointPosition.begin());
825 
826  yarp_struct.jointVelocity_isValid = speedsOk;
827  std::copy(ros_struct.velocity.begin(), ros_struct.velocity.end(), yarp_struct.jointVelocity.begin());
828 
829  yarp_struct.torque_isValid = torqueOk;
830  std::copy(ros_struct.effort.begin(), ros_struct.effort.end(), yarp_struct.torque.begin());
831 
832  // Get remaining data from HW
834  yarp_struct.motorPosition_isValid = getMotorEncoders(yarp_struct.motorPosition.data());
835  yarp_struct.motorVelocity_isValid = getMotorEncoderSpeeds(yarp_struct.motorVelocity.data());
837  yarp_struct.pwmDutycycle_isValid = getDutyCycles(yarp_struct.pwmDutycycle.data());
839  yarp_struct.controlMode_isValid = getControlModes(yarp_struct.controlMode.data());
840  yarp_struct.interactionMode_isValid = getInteractionModes(reinterpret_cast<yarp::dev::InteractionModeEnum*>(yarp_struct.interactionMode.data()));
841 
842  extendedOutputStatePort.setEnvelope(averageTime);
843  extendedOutputState_buffer.write();
844 
845  // handle state:o
846  yarp::sig::Vector& v = outputPositionStatePort.prepare();
848  std::copy(yarp_struct.jointPosition.begin(), yarp_struct.jointPosition.end(), v.begin());
849 
850  outputPositionStatePort.setEnvelope(averageTime);
851  outputPositionStatePort.write();
852  }
853 
854  if (useROS != ROS_disabled) {
855  // Data from HW have been gathered few lines before
856  JointTypeEnum jType;
857  for (size_t i = 0; i < controlledJoints; i++) {
858  getJointType(i, jType);
859  if (jType == VOCAB_JOINTTYPE_REVOLUTE) {
860  ros_struct.position[i] = convertDegreesToRadians(ros_struct.position[i]);
861  ros_struct.velocity[i] = convertDegreesToRadians(ros_struct.velocity[i]);
862  }
863  }
864 
865  ros_struct.name = jointNames;
866 
867  ros_struct.header.seq = rosMsgCounter++;
868  ros_struct.header.stamp = averageTime.getTime();
869 
870  rosPublisherPort.write(ros_struct);
871  }
872 }
const yarp::os::LogComponent & CONTROLBOARD()
bool ret
bool getJointType(int j, yarp::dev::JointTypeEnum &type) override
bool getAxisName(int j, std::string &name) override
bool getControlModes(int *modes) override
Get the current control mode (multiple joints).
bool getEncoderAccelerations(double *accs) override
Read the instantaneous acceleration of all axes.
bool getEncoderSpeeds(double *spds) override
Read the instantaneous speed of all axes.
bool getEncodersTimed(double *encs, double *t) override
Read the instantaneous acceleration of all axes.
bool getInteractionModes(int n_joints, int *joints, yarp::dev::InteractionModeEnum *modes) override
Get the current interaction mode of the robot for a set of joints, values can be stiff or compliant.
bool getMotorEncoderSpeeds(double *spds) override
Read the instantaneous speed of all motor encoders.
bool getMotorEncoders(double *encs) override
Read the position of all motor encoders.
bool getMotorEncoderAccelerations(double *accs) override
Read the instantaneous acceleration of all motor encoders.
bool getDutyCycles(double *v) override
Gets the current dutycycle of the output of the amplifier (i.e.
bool getTorques(double *t) override
Get the value of the torque for all joints (this is the feedback if you have torque sensors).
bool open(yarp::os::Searchable &prop) override
Open the device driver.
bool detachAll() override
Detach the object (you must have first called attach).
~ControlBoardWrapper() override
bool close() override
Close the device driver by deallocating all resources and closing ports.
void run() override
The thread main loop deals with writing on ports here.
bool attachAll(const yarp::dev::PolyDriverList &l) override
Attach to a list of objects.
void resize(int _deviceNum, int _maxJointsNumForDevice, WrappedDevice *_device)
void init(yarp::dev::DeviceDriver *x)
Initialization.
virtual bool initialize()
Initialize the internal data.
void init(yarp::dev::DeviceDriver *x)
Initialization.
size_t totalAxis
Definition: SubDevice.h:60
bool configure(size_t wbase, size_t wtop, size_t base, size_t top, size_t axes, const std::string &id, const std::string &_parentName)
Definition: SubDevice.cpp:23
void detach()
Definition: SubDevice.cpp:60
std::vector< DevicesLutEntry > lut
Definition: SubDevice.h:122
SubDevice * getSubdevice(size_t i)
Definition: SubDevice.h:125
size_t maxNumOfJointsInDevices
Definition: SubDevice.h:123
SubDeviceVector subdevices
Definition: SubDevice.h:121
bool view(T *&x)
Get an interface to the device driver.
Definition: DeviceDriver.h:74
void attach(yarp::os::TypedReader< yarp::os::Bottle > &source)
Attach this object to a source of messages.
Definition: DeviceDriver.h:199
Control board, encoder interface.
Definition: IEncoders.h:118
virtual bool getAxes(int *ax)=0
Get the number of controlled axes.
IRemoteCalibrator interface is meant to remotize the access of the calibration device in order to all...
A container for a device driver.
Definition: PolyDriver.h:24
bool close() override
Close the DeviceDriver.
Definition: PolyDriver.cpp:173
bool isValid() const
Check if device is valid.
Definition: PolyDriver.cpp:196
bool open(const std::string &txt)
Construct and configure a device by its common name.
Definition: PolyDriver.cpp:140
yarp::sig::VectorOf< double > motorVelocity
Definition: jointData.h:37
yarp::sig::VectorOf< double > jointAcceleration
Definition: jointData.h:33
yarp::sig::VectorOf< int > controlMode
Definition: jointData.h:47
yarp::sig::VectorOf< int > interactionMode
Definition: jointData.h:49
yarp::sig::VectorOf< double > current
Definition: jointData.h:45
yarp::sig::VectorOf< double > motorAcceleration
Definition: jointData.h:39
yarp::sig::VectorOf< double > motorPosition
Definition: jointData.h:35
yarp::sig::VectorOf< double > pwmDutycycle
Definition: jointData.h:43
yarp::sig::VectorOf< double > torque
Definition: jointData.h:41
yarp::sig::VectorOf< double > jointPosition
Definition: jointData.h:29
yarp::sig::VectorOf< double > jointVelocity
Definition: jointData.h:31
A simple collection of objects that can be described and transmitted in a portable way.
Definition: Bottle.h:74
void fromString(const std::string &text)
Initializes bottle from a string.
Definition: Bottle.cpp:204
size_type size() const
Gets the number of elements in the bottle.
Definition: Bottle.cpp:251
Value & get(size_type index) const
Reads a Value v from a certain part of the list.
Definition: Bottle.cpp:246
Bottle & findGroup(const std::string &key) const override
Gets a list corresponding to a given keyword.
Definition: Bottle.cpp:302
bool check(const std::string &key) const override
Check if there exists a property of the given name.
Definition: Bottle.cpp:277
Bottle tail() const
Get all but the first element of a bottle.
Definition: Bottle.cpp:388
bool isNull() const override
Checks if the object is invalid.
Definition: Bottle.cpp:370
std::string toString() const override
Gives a human-readable textual representation of the bottle.
Definition: Bottle.cpp:211
Value & find(const std::string &key) const override
Gets a value corresponding to a given keyword.
Definition: Bottle.cpp:287
void close() override
Stop port activity.
bool setEnvelope(PortWriter &envelope) override
Set an envelope (e.g., a timestamp) to the next message which will be sent.
bool open(const std::string &name) override
Start port operation, with a specific name, with automatically-chosen network parameters.
void interrupt() override
Interrupt any current reads or writes attached to the port.
void write(bool forceStrict=false)
Write the current object being returned by BufferedPort::prepare.
T & prepare()
Access the object which will be transmitted by the next call to yarp::os::BufferedPort::write.
The Node class.
Definition: Node.h:24
An abstraction for a periodic thread.
bool isRunning() const
Returns true when the thread is started, false otherwise.
void stop()
Call this to stop the thread, this call blocks until the thread is terminated (and releaseThread() ca...
void attach(Port &port)
Attach this buffer to a particular port.
void write(bool forceStrict=false)
Try to write the last buffer returned by PortWriterBuffer::get.
T & get()
A synonym of PortWriterBuffer::prepare.
void attach(Port &port)
Set the Port to which objects will be written.
void setReader(PortReader &reader) override
Set an external reader for port data.
Definition: Port.cpp:502
bool removeCallbackLock() override
Remove a lock on callbacks added with setCallbackLock()
Definition: Port.cpp:691
void interrupt() override
Interrupt any current reads or writes attached to the port.
Definition: Port.cpp:374
bool setEnvelope(PortWriter &envelope) override
Set an envelope (e.g., a timestamp) to the next message which will be sent.
Definition: Port.cpp:538
void close() override
Stop port activity.
Definition: Port.cpp:354
bool open(const std::string &name) override
Start port operation, with a specific name, with automatically-chosen network parameters.
Definition: Port.cpp:79
A class for storing options and configuration information.
Definition: Property.h:34
Value & find(const std::string &key) const override
Gets a value corresponding to a given keyword.
Definition: Property.cpp:1051
std::string toString() const override
Return a standard text representation of the content of the object.
Definition: Property.cpp:1069
void fromString(const std::string &txt, bool wipe=true)
Interprets a string as a list of properties.
Definition: Property.cpp:1063
void put(const std::string &key, const std::string &value)
Associate the given key with the given string.
Definition: Property.cpp:1015
bool check(const std::string &key) const override
Check if there exists a property of the given name.
Definition: Property.cpp:1041
Bottle & findGroup(const std::string &key) const override
Gets a list corresponding to a given keyword.
Definition: Property.cpp:1142
void unput(const std::string &key)
Remove the association from the given key to a value, if present.
Definition: Property.cpp:1046
bool topic(const std::string &name)
Set topic to publish to.
Definition: Publisher.h:64
void write(bool forceStrict=false)
Write the current object being returned by Publisher::prepare.
Definition: Publisher.h:149
A base class for nested structures that can be searched.
Definition: Searchable.h:66
virtual Value & find(const std::string &key) const =0
Gets a value corresponding to a given keyword.
virtual bool check(const std::string &key) const =0
Check if there exists a property of the given name.
virtual std::string toString() const =0
Return a standard text representation of the content of the object.
virtual Bottle & findGroup(const std::string &key) const =0
Gets a list corresponding to a given keyword.
An abstraction for a time stamp and/or sequence number.
Definition: Stamp.h:22
void update()
Set the timestamp to the current time, and increment the sequence number (wrapping to 0 if the sequen...
Definition: Stamp.cpp:124
double getTime() const
Get the time stamp.
Definition: Stamp.cpp:34
A single value (typically within a Bottle).
Definition: Value.h:45
virtual std::int32_t asInt32() const
Get 32-bit integer value.
Definition: Value.cpp:204
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
virtual bool isInt32() const
Checks if value is a 32-bit integer.
Definition: Value.cpp:132
virtual std::string asString() const
Get string value.
Definition: Value.cpp:234
std::vector< std::string > name
Definition: JointState.h:56
std::vector< yarp::conf::float64_t > position
Definition: JointState.h:57
std::vector< yarp::conf::float64_t > velocity
Definition: JointState.h:58
std::vector< yarp::conf::float64_t > effort
Definition: JointState.h:59
yarp::rosmsg::std_msgs::Header header
Definition: JointState.h:55
yarp::rosmsg::TickTime stamp
Definition: Header.h:45
void resize(size_t size) override
Resize the vector.
Definition: Vector.h:222
iterator begin() noexcept
Returns an iterator to the beginning of the VectorOf.
Definition: Vector.h:455
T * data()
Return a pointer to the first element of the vector.
Definition: Vector.h:207
iterator end() noexcept
Returns an iterator to the end of the VectorOf.
Definition: Vector.h:462
#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
An interface for the device drivers.
@ VOCAB_JOINTTYPE_REVOLUTE
Definition: IAxisInfo.h:27
An interface to the operating system, including Port based communication.
Signal processing.
Definition: Image.h:22
The main, catch-all namespace for YARP.
Definition: dirs.h:16
@ ROS_only
Definition: yarpRosHelper.h:20
@ ROS_config_error
Definition: yarpRosHelper.h:17
@ ROS_enabled
Definition: yarpRosHelper.h:19
@ ROS_disabled
Definition: yarpRosHelper.h:18
double convertDegreesToRadians(double degrees)
convert degrees to radiants for ROS messages
Definition: yarpRosHelper.h:27