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
8
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
23using namespace yarp::os;
24using namespace yarp::dev;
25using namespace yarp::dev::impl;
26using namespace yarp::sig;
27
28
31{
32 streaming_parser.init(this);
33 RPC_parser.init(this);
34}
35
36void 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
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
89bool 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
131bool 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
224bool 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
265bool 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.7 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
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
434bool 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();
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.
553bool 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
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
628void ControlBoardWrapper::calculateMaxNumOfJointsInDevices()
629{
631
632 for (size_t d = 0; d < device.subdevices.size(); d++) {
636 }
637 }
638}
639
640bool 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
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);
817 yarp_struct.current.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());
837 yarp_struct.pwmDutycycle_isValid = getDutyCycles(yarp_struct.pwmDutycycle.data());
839 yarp_struct.controlMode_isValid = getControlModes(yarp_struct.controlMode.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()
constexpr double default_period
Definition: FakeOdometry.h:14
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
size_t maxNumOfJointsInDevices
Definition: SubDevice.h:123
SubDeviceVector subdevices
Definition: SubDevice.h:121
SubDevice * getSubdevice(size_t i)
Definition: SubDevice.h:125
bool view(T *&x)
Get an interface to the device driver.
Definition: DeviceDriver.h:88
void attach(yarp::os::TypedReader< yarp::os::Bottle > &source)
Attach this object to a source of messages.
Definition: DeviceDriver.h:219
Control board, encoder interface.
Definition: IEncoders.h:116
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:23
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:35
yarp::sig::VectorOf< double > jointAcceleration
Definition: jointData.h:31
yarp::sig::VectorOf< int > controlMode
Definition: jointData.h:45
yarp::sig::VectorOf< int > interactionMode
Definition: jointData.h:47
yarp::sig::VectorOf< double > current
Definition: jointData.h:43
yarp::sig::VectorOf< double > motorAcceleration
Definition: jointData.h:37
yarp::sig::VectorOf< double > motorPosition
Definition: jointData.h:33
yarp::sig::VectorOf< double > pwmDutycycle
Definition: jointData.h:41
yarp::sig::VectorOf< double > torque
Definition: jointData.h:39
yarp::sig::VectorOf< double > jointPosition
Definition: jointData.h:27
yarp::sig::VectorOf< double > jointVelocity
Definition: jointData.h:29
A simple collection of objects that can be described and transmitted in a portable way.
Definition: Bottle.h:64
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:23
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.
T & get()
A synonym of PortWriterBuffer::prepare.
void write(bool forceStrict=false)
Try to write the last buffer returned by PortWriterBuffer::get.
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:511
bool removeCallbackLock() override
Remove a lock on callbacks added with setCallbackLock()
Definition: Port.cpp:700
void interrupt() override
Interrupt any current reads or writes attached to the port.
Definition: Port.cpp:383
bool setEnvelope(PortWriter &envelope) override
Set an envelope (e.g., a timestamp) to the next message which will be sent.
Definition: Port.cpp:547
void close() override
Stop port activity.
Definition: Port.cpp:363
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:33
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:63
void write(bool forceStrict=false)
Write the current object being returned by Publisher::prepare.
Definition: Publisher.h:148
A base class for nested structures that can be searched.
Definition: Searchable.h:63
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 Value & find(const std::string &key) const =0
Gets a value corresponding to a given keyword.
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:21
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:43
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:220
iterator begin() noexcept
Returns an iterator to the beginning of the VectorOf.
Definition: Vector.h:453
T * data()
Return a pointer to the first element of the vector.
Definition: Vector.h:205
iterator end() noexcept
Returns an iterator to the end of the VectorOf.
Definition: Vector.h:460
#define yCInfo(component,...)
Definition: LogComponent.h:171
#define yCError(component,...)
Definition: LogComponent.h:213
#define yCWarning(component,...)
Definition: LogComponent.h:192
#define yCDebug(component,...)
Definition: LogComponent.h:128
For streams capable of holding different kinds of content, check what they actually have.
@ VOCAB_JOINTTYPE_REVOLUTE
Definition: IAxisInfo.h:24
An interface to the operating system, including Port based communication.
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