YARP
Yet Another Robot Platform
FrameTransformClient.cpp
Go to the documentation of this file.
1 /*
2  * SPDX-FileCopyrightText: 2006-2021 Istituto Italiano di Tecnologia (IIT)
3  * SPDX-License-Identifier: LGPL-2.1-or-later
4  */
5 
6 #define _USE_MATH_DEFINES
7 #include <cmath>
8 
9 #include "FrameTransformClient.h"
10 
11 #include <yarp/os/Log.h>
12 #include <yarp/os/LogComponent.h>
13 #include <yarp/os/LogStream.h>
14 
15 #include <yarp/math/Math.h>
16 
17 #include <cmrc/cmrc.hpp>
18 #include <mutex>
19 CMRC_DECLARE(frameTransformRC);
20 
21 using namespace yarp::dev;
22 using namespace yarp::os;
23 using namespace yarp::sig;
24 using namespace yarp::math;
25 
26 namespace {
27 YARP_LOG_COMPONENT(FRAMETRANSFORMCLIENT, "yarp.device.FrameTransformClient")
28 }
29 #define LOG_THROTTLE_PERIOD 1.0
30 
31 //------------------------------------------------------------------------------------------------------------------------------
33 {
34  std::lock_guard<std::mutex> lock (m_rpc_mutex);
36  yarp::os::Bottle out;
37  bool ok = in.read(connection);
38  if (!ok) {
39  return false;
40  }
41 
42  std::string request = in.get(0).asString();
43  if (request == "help")
44  {
45  out.addVocab32("many");
46  out.addString("get_transform <src> <dst>: print the transform from <src> to <dst>");
47  out.addString("list_transforms: print all the stored frame transforms");
48  out.addString("list_frames: print all the available reference frames");
49  out.addString("list_ports: print all the opened ports for transform broadcasting");
50  out.addString("publish_transform <src> <dst> <portname> [format]: opens a port to publish transform from src to dst");
51  out.addString("unpublish_transform <portname>: closes a previously opened port to publish a transform");
52  out.addString("unpublish_all: closes a all previously opened ports to publish a transform");
53  out.addString("set_static_transform_rad <src> <dst> <x> <y> <z> <roll> <pitch> <yaw>': create a static transform (angles in radians)");
54  out.addString("set_static_transform_deg <src> <dst> <x> <y> <z> <roll> <pitch> <yaw>': create a static transform (angles in degrees)");
55  out.addString("delete_static_transform <src> <dst>': delete a static transform");
56  out.addString("generate_view <option>': generate a frames.pdf file showing the transform tree diagram.");
57  out.addString(" The following values are valid for option (default=none)");
58  out.addString(" 'show_rpy': show rotation as rpy angles");
59  out.addString(" 'show_quaterion:'show rotation as a quaternion");
60  out.addString(" 'show_matrix:'show rotation as a 3x3 rotation matrix");
61  }
62  else if (request == "list_frames")
63  {
64  std::vector<std::string> v;
65  this->getAllFrameIds(v);
66  out.addVocab32("many");
67  out.addString("List of available reference frames:");
68  int count = 0;
69  for (auto& vec : v)
70  {
71  count++;
72  std::string str = std::to_string(count) + "- " + vec;
73  out.addString(str.c_str());
74  }
75  }
76  else if (request == "get_transform")
77  {
78  std::string src = in.get(1).asString();
79  std::string dst = in.get(2).asString();
80  out.addVocab32("many");
82  this->getTransform(src, dst, m);
83  out.addString("Transform from " + src + " to " + dst + " is: ");
84  out.addString(m.toString());
85  }
86  else if (request == "list_ports")
87  {
88  out.addVocab32("many");
89  if (m_array_of_ports.size()==0)
90  {
91  out.addString("No ports are currently active");
92  }
93  for (auto& m_array_of_port : m_array_of_ports)
94  {
95  if (m_array_of_port)
96  {
97  std::string s = m_array_of_port->port.getName() + " "+ m_array_of_port->transform_src + " -> " + m_array_of_port->transform_dst;
98  out.addString(s);
99  }
100  }
101  }
102  else if (request == "publish_transform")
103  {
104  out.addVocab32("many");
105  std::string src = in.get(1).asString();
106  std::string dst = in.get(2).asString();
107  std::string port_name = in.get(3).asString();
108  std::string format_s = "matrix";
109  broadcast_port_t::format_t eformat = broadcast_port_t::format_t::matrix;
110  if (in.size() == 5) {format_s = in.get(4).asString();}
111 
112  if (format_s == "matrix") {eformat = broadcast_port_t::format_t::matrix;}
113  else {yCError(FRAMETRANSFORMCLIENT) << "Invalid format" << format_s << "using format `matrix`. Only `matrix` is currently supported."; }
114 
115  if (port_name[0] == '/') {
116  port_name.erase(port_name.begin());
117  }
118  std::string full_port_name = m_local_name + "/" + port_name;
119 
120  //print a warning if the frames do not exists yet
121  if (this->frameExists(src) == false)
122  {
123  out.addString("Requested src frame " + src + " does not exists.");
124  yCWarning(FRAMETRANSFORMCLIENT, "Requested src frame %s does not exists.", src.c_str());
125  }
126  if (this->frameExists(dst) == false)
127  {
128  out.addString("Requested dst frame " + dst + " does not exists.");
129  yCWarning(FRAMETRANSFORMCLIENT, "Requested fst frame %s does not exists.", dst.c_str());
130  }
131 
132  //check if the port is already active in yarp
133  bool port_already_exists = yarp::os::Network::exists(full_port_name);
134  if (port_already_exists) { yCError(FRAMETRANSFORMCLIENT, "Requested port already exists!"); }
135 
136  //check if the port is already registered by the system
137  bool bcast_already_exists = false;
138  for (auto& m_array_of_port : m_array_of_ports)
139  {
140  if (m_array_of_port && m_array_of_port->port.getName() == full_port_name)
141  {
142  bcast_already_exists |= true;
143  break;
144  }
145  }
146 
147  //create the broadcaster
148  if (bcast_already_exists == false && port_already_exists == false)
149  {
150  auto* b = new broadcast_port_t;
151  b->transform_src = src;
152  b->transform_dst = dst;
153  b->format = eformat;
154  bool pret = b->port.open(full_port_name);
155  if (pret)
156  {
157  out.addString("Operation complete. Port " + full_port_name + " opened.");
158  m_array_of_ports.push_back(b);
159  if (m_array_of_ports.size() == 1) {
160  this->start();
161  }
162  }
163  else
164  {
165  delete b;
166  out.addString("Operation failed. Unable to open port " + full_port_name + ".");
167  }
168  }
169  else
170  {
171  out.addString("unable to perform operation");
172  }
173  }
174  else if (request == "unpublish_all")
175  {
176  for (auto& m_array_of_port : m_array_of_ports)
177  {
178  m_array_of_port->port.close();
179  delete m_array_of_port;
180  m_array_of_port=nullptr;
181  }
182  m_array_of_ports.clear();
183  if (m_array_of_ports.size() == 0) {
184  this->askToStop();
185  }
186  out.addString("Operation complete");
187  }
188  else if (request == "unpublish_transform")
189  {
190  bool ret = false;
191  std::string port_name = in.get(1).asString();
192  if (port_name[0] == '/') {
193  port_name.erase(port_name.begin());
194  }
195  std::string full_port_name = m_local_name + "/" + port_name;
196  for (auto it = m_array_of_ports.begin(); it != m_array_of_ports.end(); it++)
197  {
198  if ((*it)->port.getName() == port_name)
199  {
200  (*it)->port.close();
201  delete (*it);
202  (*it)=nullptr;
203  m_array_of_ports.erase(it);
204  ret = true;
205  break;
206  }
207  }
208  if (ret)
209  {
210  out.addString("Port " + full_port_name + " has been closed.");
211  }
212  else
213  {
214  out.addString("Port " + full_port_name + " was not found.");
215  }
216  if (m_array_of_ports.size() == 0) {
217  this->askToStop();
218  }
219  }
220  else if (request == "set_static_transform_rad" ||
221  request == "set_static_transform_deg")
222  {
224  t.src_frame_id = in.get(1).asString();
225  t.dst_frame_id = in.get(2).asString();
226  t.translation.tX = in.get(3).asFloat64();
227  t.translation.tY = in.get(4).asFloat64();
228  t.translation.tZ = in.get(5).asFloat64();
229  if (request == "set_static_transform_rad")
230  {
231  t.rotFromRPY(in.get(6).asFloat64(),
232  in.get(7).asFloat64(),
233  in.get(8).asFloat64());
234  }
235  else if (request == "set_static_transform_deg")
236  {
237  t.rotFromRPY(in.get(6).asFloat64() * 180 / M_PI,
238  in.get(7).asFloat64() * 180 / M_PI,
239  in.get(8).asFloat64() * 180 / M_PI);
240  }
241  t.timestamp = yarp::os::Time::now();
242  bool ret = this->setTransformStatic(t.src_frame_id,t.dst_frame_id,t.toMatrix());
243  if (ret == true)
244  {
245  yCInfo(FRAMETRANSFORMCLIENT) << "set_static_transform done";
246  out.addString("set_static_transform done");
247  }
248  else
249  {
250  yCError(FRAMETRANSFORMCLIENT) << "read(): something strange happened";
251  out.addString("error");
252  }
253  }
254  else if (request == "generate_view")
255  {
256  m_show_transforms_in_diagram = show_transforms_in_diagram_t::do_not_show;
257  if (in.get(1).asString() == "show_quaternion") {
258  m_show_transforms_in_diagram = show_transforms_in_diagram_t::show_quaternion;
259  } else if (in.get(1).asString() == "show_matrix") {
260  m_show_transforms_in_diagram = show_transforms_in_diagram_t::show_matrix;
261  } else if (in.get(1).asString() == "show_rpy") {
262  m_show_transforms_in_diagram = show_transforms_in_diagram_t::show_rpy;
263  }
264  priv_generate_view();
265  out.addString("ok");
266  }
267  else if (request == "delete_static_transform")
268  {
269  std::string src = in.get(1).asString();
270  std::string dst = in.get(2).asString();
271  //@@@check if the transform is static?
272  this->deleteTransform(src,dst);
273  out.addString("delete_static_transform done");
274  }
275  else
276  {
277  yCError(FRAMETRANSFORMCLIENT, "Invalid vocab received in FrameTransformClient");
278  out.clear();
279  out.addVocab32(VOCAB_ERR);
280  out.addString("Invalid command name");
281  }
282 
283  yarp::os::ConnectionWriter *returnToSender = connection.getWriter();
284  if (returnToSender != nullptr)
285  {
286  out.write(*returnToSender);
287  }
288  else
289  {
290  yCError(FRAMETRANSFORMCLIENT) << "Invalid return to sender";
291  }
292  return true;
293 }
294 
296 {
297  yCWarning(FRAMETRANSFORMCLIENT) << "The 'FrameTransformClient' device is experimental and could be modified without any warning";
298 
299  yarp::os::Property cfg;
300  cfg.fromString(config.toString());
301 
302  std::string configuration_to_open;
303  std::string innerFilePath="config_xml/ftc_local_only.xml";
304  auto fs = cmrc::frameTransformRC::get_filesystem();
305  if(cfg.check("filexml_option")) { innerFilePath="config_xml/"+cfg.find("filexml_option").toString();}
306  cfg.unput("filexml_option");
307  auto xmlFile = fs.open(innerFilePath);
308  for(const auto& lemma : xmlFile)
309  {
310  configuration_to_open += lemma;
311  }
312 
313  std::string m_local_rpcUser = "/ftClient/rpc";
314  if (cfg.check("local_rpc")) { m_local_rpcUser=cfg.find("local_rpc").toString();}
315  cfg.unput("local_rpc");
316 
318  yarp::robotinterface::XMLReaderResult result = reader.getRobotFromString(configuration_to_open, cfg);
319  yCAssert(FRAMETRANSFORMCLIENT, result.parsingIsSuccessful);
320 
321  m_robot = std::move(result.robot);
322 
323  if (!m_robot.enterPhase(yarp::robotinterface::ActionPhaseStartup)) {
324  return false;
325  }
326 
327  if (!m_robot.enterPhase(yarp::robotinterface::ActionPhaseRun)) {
328  return false;
329  }
330 
331  std::string setdeviceName = "ftc_storage";
332  if (m_robot.hasParam("setDeviceName")) { setdeviceName = m_robot.findParam("setDeviceName");}
333  yCAssert(FRAMETRANSFORMCLIENT, m_robot.hasDevice(setdeviceName));
334  auto* polyset = m_robot.device(setdeviceName).driver();
335  yCAssert(FRAMETRANSFORMCLIENT, polyset);
336  polyset->view(m_ift_set);
337  yCAssert(FRAMETRANSFORMCLIENT, m_ift_set);
338 
339  std::string getdeviceName = "ftc_storage";
340  if (m_robot.hasParam("getDeviceName")) {getdeviceName = m_robot.findParam("getDeviceName");}
341  yCAssert(FRAMETRANSFORMCLIENT, m_robot.hasDevice(getdeviceName));
342  auto* polyget = m_robot.device(getdeviceName).driver();
343  yCAssert(FRAMETRANSFORMCLIENT, polyget);
344  polyget->view(m_ift_get);
345  yCAssert(FRAMETRANSFORMCLIENT, m_ift_get);
346  polyget->view(m_ift_util);
347  yCAssert(FRAMETRANSFORMCLIENT, m_ift_util);
348 
349  if (config.check("period"))
350  {
351  m_period = config.find("period").asFloat64();
352  this->setPeriod(m_period);
353  }
354 
355  if (!m_rpc_InterfaceToUser.open(m_local_rpcUser))
356  {
357  yCError(FRAMETRANSFORMCLIENT,"Failed to open rpc port");
358  }
359 
360  m_rpc_InterfaceToUser.setReader(*this);
361 
362  return true;
363 }
364 
366 {
368  m_robot.enterPhase(yarp::robotinterface::ActionPhaseShutdown);
369  m_rpc_InterfaceToUser.close();
370  return true;
371 }
372 
373 bool FrameTransformClient::allFramesAsString(std::string &all_frames)
374 {
375  FrameTransformContainer* p_cont = nullptr;
376  bool br = m_ift_util->getInternalContainer(p_cont);
377  if (!br || p_cont == nullptr) { yCError(FRAMETRANSFORMCLIENT) << "Failure"; return false; }
378 
379  //protect the internal container from concurrent access
380  std::lock_guard<std::recursive_mutex> l(p_cont->m_trf_mutex);
381 
382  //process data
383  for (auto it = p_cont->begin(); it != p_cont->end(); it++)
384  {
385  all_frames += it->toString() + " ";
386  }
387  return true;
388 }
389 
390 FrameTransformClient::ConnectionType FrameTransformClient::priv_getConnectionType(const std::string &target_frame, const std::string &source_frame, std::string* commonAncestor = nullptr)
391 {
392  if (target_frame == source_frame) {return ConnectionType::IDENTITY;}
393 
394  FrameTransformContainer* p_cont = nullptr;
395  bool br = m_ift_util->getInternalContainer(p_cont);
396  if (!br || p_cont == nullptr) { yCError(FRAMETRANSFORMCLIENT) << "Failure"; return ConnectionType::DISCONNECTED; }
397 
398  size_t i, j;
399  std::vector<std::string> tar2root_vec;
400  std::vector<std::string> src2root_vec;
401  std::string ancestor, child;
402  child = target_frame;
403 
404  //protect the internal container from concurrent access
405  std::lock_guard<std::recursive_mutex> l(p_cont->m_trf_mutex);
406 
407  //process data
408  while(getParent(child, ancestor))
409  {
410  if(ancestor == source_frame)
411  {
412  return ConnectionType::DIRECT;
413  }
414 
415  tar2root_vec.push_back(ancestor);
416  child = ancestor;
417  }
418  child = source_frame;
419  while(getParent(child, ancestor))
420  {
421  if(ancestor == target_frame)
422  {
423  return ConnectionType::INVERSE;
424  }
425 
426  src2root_vec.push_back(ancestor);
427  child = ancestor;
428  }
429 
430  for(i = 0; i < tar2root_vec.size(); i++)
431  {
432  std::string a;
433  a = tar2root_vec[i];
434  for(j = 0; j < src2root_vec.size(); j++)
435  {
436  if(a == src2root_vec[j])
437  {
438  if(commonAncestor)
439  {
440  *commonAncestor = a;
441  }
442  return ConnectionType::UNDIRECT;
443  }
444  }
445  }
446 
447  return ConnectionType::DISCONNECTED;
448 }
449 
450 bool FrameTransformClient::canTransform(const std::string &target_frame, const std::string &source_frame)
451 {
452  return priv_getConnectionType(target_frame, source_frame) != ConnectionType::DISCONNECTED;
453 }
454 
456 {
457  if (m_ift_set)
458  {
459  return m_ift_set->clearAll();
460  }
461  yCError(FRAMETRANSFORMCLIENT, "clear(): interface not available");
462  return false;
463 }
464 
465 bool FrameTransformClient::frameExists(const std::string &frame_id)
466 {
467  FrameTransformContainer* p_cont = nullptr;
468  bool br = m_ift_util->getInternalContainer(p_cont);
469  if (!br || p_cont == nullptr) { yCError(FRAMETRANSFORMCLIENT) << "Failure"; return false; }
470 
471  //protect the internal container from concurrent access
472  std::lock_guard<std::recursive_mutex> l(p_cont->m_trf_mutex);
473 
474  //process data
475  for (auto it = p_cont->begin(); it != p_cont->end(); it++)
476  {
477  if (it->src_frame_id == frame_id) { return true; }
478  if (it->dst_frame_id == frame_id) { return true; }
479  }
480 
481  // condition reached if not found or container is empty
482  return false;
483 }
484 
485 bool FrameTransformClient::getAllFrameIds(std::vector<std::string> &ids)
486 {
487  FrameTransformContainer* p_cont = nullptr;
488  bool br = m_ift_util->getInternalContainer(p_cont);
489  if (!br || p_cont == nullptr) { yCError(FRAMETRANSFORMCLIENT) << "Failure"; return false; }
490 
491  //protect the internal container from concurrent access
492  std::lock_guard<std::recursive_mutex> l(p_cont->m_trf_mutex);
493 
494  //process data
495  for (auto it = p_cont->begin(); it != p_cont->end(); it++)
496  {
497  bool found = false;
498  for (const auto& id : ids)
499  {
500  if (it->src_frame_id == id) { found = true; break; }
501  }
502  if (found == false) {
503  ids.push_back(it->src_frame_id);
504  }
505  }
506 
507  for (auto it = p_cont->begin(); it != p_cont->end(); it++)
508  {
509  bool found = false;
510  for (const auto& id : ids)
511  {
512  if (it->dst_frame_id == id) { found = true; break; }
513  }
514  if (found == false) {
515  ids.push_back(it->dst_frame_id);
516  }
517  }
518 
519  return true;
520 }
521 
522 bool FrameTransformClient::getParent(const std::string &frame_id, std::string &parent_frame_id)
523 {
524  FrameTransformContainer* p_cont = nullptr;
525  bool br = m_ift_util->getInternalContainer(p_cont);
526  if (!br || p_cont == nullptr) { yCError(FRAMETRANSFORMCLIENT) << "Failure"; return false; }
527 
528  //protect the internal container from concurrent access
529  std::lock_guard<std::recursive_mutex> l(p_cont->m_trf_mutex);
530 
531  //process data
532  for (auto it = p_cont->begin(); it != p_cont->end(); it++)
533  {
534  std::string par(it->dst_frame_id);
535  if (it->dst_frame_id == frame_id)
536  {
537  parent_frame_id = it->src_frame_id;
538  return true;
539  }
540  }
541  return false;
542 }
543 
544 bool FrameTransformClient::priv_canExplicitTransform(const std::string& target_frame_id, const std::string& source_frame_id) const
545 {
546  FrameTransformContainer* p_cont = nullptr;
547  bool br = m_ift_util->getInternalContainer(p_cont);
548  if (!br || p_cont == nullptr) {yCError(FRAMETRANSFORMCLIENT) << "Failure"; return false; }
549 
550  //protect the internal container from concurrent access
551  std::lock_guard<std::recursive_mutex> l(p_cont->m_trf_mutex);
552 
553  //process data
554  for (auto it = p_cont->begin(); it != p_cont->end(); it++)
555  {
556  if (it->dst_frame_id == target_frame_id && it->src_frame_id == source_frame_id)
557  {
558  return true;
559  }
560  }
561  return false;
562 }
563 
564 bool FrameTransformClient::priv_getChainedTransform(const std::string& target_frame_id, const std::string& source_frame_id, yarp::sig::Matrix& transform) const
565 {
566  FrameTransformContainer* p_cont = nullptr;
567  bool br = m_ift_util->getInternalContainer(p_cont);
568  if (!br || p_cont == nullptr) { yCError(FRAMETRANSFORMCLIENT) << "Failure"; return false; }
569 
570  //protect the internal container from concurrent access
571  std::lock_guard<std::recursive_mutex> l(p_cont->m_trf_mutex);
572 
573  //process data
574  for (auto it = p_cont->begin(); it != p_cont->end(); it++)
575  {
576  if (it->dst_frame_id == target_frame_id)
577  {
578  if (it->src_frame_id == source_frame_id)
579  {
580  transform = it->toMatrix();
581  return true;
582  }
583  else
584  {
586  if (priv_getChainedTransform(it->src_frame_id, source_frame_id, m))
587  {
588  transform = m * it->toMatrix();
589  return true;
590  }
591  }
592  }
593  }
594  return false;
595 }
596 
597 bool FrameTransformClient::getTransform(const std::string& target_frame_id, const std::string& source_frame_id, yarp::sig::Matrix& transform)
598 {
599  ConnectionType ct;
600  std::string ancestor;
601  ct = priv_getConnectionType(target_frame_id, source_frame_id, &ancestor);
602  if (ct == ConnectionType::DIRECT)
603  {
604  return priv_getChainedTransform(target_frame_id, source_frame_id, transform);
605  }
606  else if (ct == ConnectionType::INVERSE)
607  {
608  yarp::sig::Matrix m(4, 4);
609  priv_getChainedTransform(source_frame_id, target_frame_id, m);
610  transform = yarp::math::SE3inv(m);
611  return true;
612  }
613  else if(ct == ConnectionType::UNDIRECT)
614  {
615  yarp::sig::Matrix root2tar(4, 4), root2src(4, 4);
616  priv_getChainedTransform(source_frame_id, ancestor, root2src);
617  priv_getChainedTransform(target_frame_id, ancestor, root2tar);
618  transform = yarp::math::SE3inv(root2src) * root2tar;
619  return true;
620  }
621  else if (ct == ConnectionType::IDENTITY)
622  {
623  yarp::sig::Matrix tmp(4, 4); tmp.eye();
624  transform = tmp;
625  return true;
626  }
627 
628  yCErrorThrottle(FRAMETRANSFORMCLIENT, LOG_THROTTLE_PERIOD) << "getTransform(): Frames " << source_frame_id << " and " << target_frame_id << " are not connected";
629  return false;
630 }
631 
632 bool FrameTransformClient::setTransform(const std::string& target_frame_id, const std::string& source_frame_id, const yarp::sig::Matrix& transform)
633 {
634  if(target_frame_id == source_frame_id)
635  {
636  yCErrorThrottle(FRAMETRANSFORMCLIENT, LOG_THROTTLE_PERIOD) << "setTransform(): Invalid transform detected.\n" \
637  "\t Source frame and target frame are both equal to " << source_frame_id;
638  return false;
639  }
640 
641  if (!priv_canExplicitTransform(target_frame_id, source_frame_id) && canTransform(target_frame_id, source_frame_id))
642  {
643  yCErrorThrottle(FRAMETRANSFORMCLIENT, LOG_THROTTLE_PERIOD) << "setTransform(): Such transform already exist by chaining transforms";
644  return false;
645  }
646 
648  yarp::os::Bottle resp;
650 
651  if (!tf.fromMatrix(transform))
652  {
653  yCErrorThrottle(FRAMETRANSFORMCLIENT, LOG_THROTTLE_PERIOD) << "setTransform(): Wrong matrix format, it has to be 4 by 4";
654  return false;
655  }
656 
657  tf.src_frame_id = source_frame_id;
658  tf.dst_frame_id = target_frame_id;
659  tf.isStatic = false;
660  tf.timestamp = yarp::os::Time::now();
661 
662  if (m_ift_set)
663  {
664  return m_ift_set->setTransform(tf);
665  }
666  yCError(FRAMETRANSFORMCLIENT, "setTransform(): interface not available");
667  return false;
668 }
669 
670 bool FrameTransformClient::setTransformStatic(const std::string &target_frame_id, const std::string &source_frame_id, const yarp::sig::Matrix &transform)
671 {
672  if(target_frame_id == source_frame_id)
673  {
674  yCErrorThrottle(FRAMETRANSFORMCLIENT, LOG_THROTTLE_PERIOD) << "setTransformStatic(): Invalid transform detected.\n" \
675  "\t Source frame and target frame are both equal to " << source_frame_id;
676  return false;
677  }
678 
679  if (canTransform(target_frame_id, source_frame_id))
680  {
681  yCErrorThrottle(FRAMETRANSFORMCLIENT, LOG_THROTTLE_PERIOD) << "setTransform(): Such static transform already exist, directly or by chaining transforms";
682  return false;
683  }
684 
686  if (!tf.fromMatrix(transform))
687  {
688  yCErrorThrottle(FRAMETRANSFORMCLIENT, LOG_THROTTLE_PERIOD) << "setTransform(): Wrong matrix format, it has to be 4 by 4";
689  return false;
690  }
691  tf.src_frame_id = source_frame_id;
692  tf.dst_frame_id = target_frame_id;
693  tf.isStatic = true;
694  tf.timestamp= yarp::os::Time::now();
695 
696  if (m_ift_set)
697  {
698  return m_ift_set->setTransform(tf);
699  }
700  yCError(FRAMETRANSFORMCLIENT, "setTransformStatic(): interface not available");
701  return false;
702 }
703 
704 bool FrameTransformClient::deleteTransform(const std::string &target_frame_id, const std::string &source_frame_id)
705 {
706  if (m_ift_set)
707  {
708  return m_ift_set->deleteTransform(target_frame_id, source_frame_id);
709  }
710  yCError(FRAMETRANSFORMCLIENT, "deleteTransform(): interface not available");
711  return false;
712 }
713 
714 bool FrameTransformClient::transformPoint(const std::string &target_frame_id, const std::string &source_frame_id, const yarp::sig::Vector &input_point, yarp::sig::Vector &transformed_point)
715 {
716  if (input_point.size() != 3)
717  {
718  yCErrorThrottle(FRAMETRANSFORMCLIENT, LOG_THROTTLE_PERIOD) << "Only 3 dimensional vector allowed.";
719  return false;
720  }
721  yarp::sig::Matrix m(4, 4);
722  if (!getTransform(target_frame_id, source_frame_id, m))
723  {
724  yCErrorThrottle(FRAMETRANSFORMCLIENT, LOG_THROTTLE_PERIOD) << "No transform found between source '" << target_frame_id << "' and target '" << source_frame_id << "'";
725  return false;
726  }
727  yarp::sig::Vector in = input_point;
728  in.push_back(1);
729  transformed_point = m * in;
730  transformed_point.pop_back();
731  return true;
732 }
733 
734 bool FrameTransformClient::transformPose(const std::string &target_frame_id, const std::string &source_frame_id, const yarp::sig::Vector &input_pose, yarp::sig::Vector &transformed_pose)
735 {
736  if (input_pose.size() != 6)
737  {
738  yCErrorThrottle(FRAMETRANSFORMCLIENT, LOG_THROTTLE_PERIOD) << "Only 6 dimensional vector (3 axes + roll pith and yaw) allowed.";
739  return false;
740  }
741  if (transformed_pose.size() != 6)
742  {
743  yCWarning(FRAMETRANSFORMCLIENT, "transformPose(): Performance warning: size transformed_pose should be 6, resizing.");
744  transformed_pose.resize(6, 0.0);
745  }
746  yarp::sig::Matrix m(4, 4);
747  if (!getTransform(target_frame_id, source_frame_id, m))
748  {
749  yCErrorThrottle(FRAMETRANSFORMCLIENT, LOG_THROTTLE_PERIOD) << "No transform found between source '" << target_frame_id << "' and target '" << source_frame_id << "'";
750  return false;
751  }
753  t.transFromVec(input_pose[0], input_pose[1], input_pose[2]);
754  t.rotFromRPY(input_pose[3], input_pose[4], input_pose[5]);
755  t.fromMatrix(m * t.toMatrix());
756  transformed_pose[0] = t.translation.tX;
757  transformed_pose[1] = t.translation.tY;
758  transformed_pose[2] = t.translation.tZ;
759 
760  yarp::sig::Vector rot;
761  rot = t.getRPYRot();
762  transformed_pose[3] = rot[0];
763  transformed_pose[4] = rot[1];
764  transformed_pose[5] = rot[2];
765  return true;
766 }
767 
768 bool FrameTransformClient::transformQuaternion(const std::string &target_frame_id, const std::string &source_frame_id, const yarp::math::Quaternion &input_quaternion, yarp::math::Quaternion &transformed_quaternion)
769 {
770  yarp::sig::Matrix m(4, 4);
771  if (!getTransform(target_frame_id, source_frame_id, m))
772  {
773  yCErrorThrottle(FRAMETRANSFORMCLIENT, LOG_THROTTLE_PERIOD) << "No transform found between source '" << target_frame_id << "' and target '" << source_frame_id <<"'";
774  return false;
775  }
777  t.rotation=input_quaternion;
778  transformed_quaternion.fromRotationMatrix(m * t.toMatrix());
779  return true;
780 }
781 
782 bool FrameTransformClient::waitForTransform(const std::string &target_frame_id, const std::string &source_frame_id, const double &timeout)
783 {
784  //loop until canTransform == true or timeout expires
785  double start = yarp::os::SystemClock::nowSystem();
786  while (!canTransform(target_frame_id, source_frame_id))
787  {
788  if (yarp::os::SystemClock::nowSystem() - start > timeout)
789  {
790  yCError(FRAMETRANSFORMCLIENT) << "waitForTransform(): timeout expired";
791  return false;
792  }
794  }
795  return true;
796 }
797 
799  m_period(0.01)
800 {
801 }
802 
804 
806 {
807  yCTrace(FRAMETRANSFORMCLIENT, "Thread started");
808  return true;
809 }
810 
812 {
813  yCTrace(FRAMETRANSFORMCLIENT, "Thread stopped");
814 }
815 
817 {
818  std::lock_guard<std::mutex> lock (m_rpc_mutex);
819  if (m_array_of_ports.size()==0)
820  {
821  return;
822  }
823 
824  for (auto& m_array_of_port : m_array_of_ports)
825  {
826  if (m_array_of_port)
827  {
828  std::string src = m_array_of_port->transform_src;
829  std::string dst = m_array_of_port->transform_dst;
831  this->getTransform(src, dst, m);
832  if (m_array_of_port->format == broadcast_port_t::format_t::matrix)
833  {
834  m_array_of_port->port.write(m);
835  }
836  else
837  {
838  yCErrorThrottle(FRAMETRANSFORMCLIENT, LOG_THROTTLE_PERIOD) << "Unknown format requested: " << (int)(m_array_of_port->format);
839  }
840  }
841  }
842 }
843 
845 {
846  if (t == nullptr) {
847  return "";
848  }
849 
851  {
852  return "";
853  }
855  {
856  return std::string(",label=\" ") + t->toString(FrameTransform::display_transform_mode_t::rotation_as_quaternion) + "\"";
857  }
859  {
860  return std::string(",label=\" ") + t->toString(FrameTransform::display_transform_mode_t::rotation_as_matrix) + "\"";
861  }
863  {
864  return std::string(",label=\" ") + t->toString(FrameTransform::display_transform_mode_t::rotation_as_rpy) + "\"";
865  }
866 
867  yCError(FRAMETRANSFORMCLIENT) << "get_matrix_as_text() invalid option";
868  return "";
869  /*
870  //this is a test to use Latek display
871  std::string s = "\\begin{ bmatrix } \
872  1 & 2 & 3\\ \
873  a & b & c \
874  \\end{ bmatrix }";
875  */
876 }
877 
878 
880 {
881  FrameTransformContainer* p_cont = nullptr;
882  bool br = m_ift_util->getInternalContainer(p_cont);
883  if (!br || p_cont == nullptr) { yCError(FRAMETRANSFORMCLIENT) << "Failure"; return false; }
884 
885  //protect the internal container from concurrent access
886  std::lock_guard<std::recursive_mutex> l(p_cont->m_trf_mutex);
887 
888  //process data
889  std::string dot_string = "digraph G { ";
890  for (auto it = p_cont->begin(); it != p_cont->end(); it++)
891  {
892  FrameTransform t = *it;
893  std::string edge_text = priv_get_matrix_as_text(&t);
894  std::string trf_text = it->src_frame_id + "->" +
895  it->dst_frame_id + " " +
896  "[color = black]";
897  dot_string += trf_text + '\n';
898  }
899 
900  std::string legend = "\n\
901  rankdir=LR\n\
902  node[shape=plaintext]\n\
903  subgraph cluster_01 {\n\
904  label = \"Legend\";\n\
905  key[label=<<table border=\"0\" cellpadding=\"2\" cellspacing=\"0\" cellborder=\"0\">\n\
906  <tr><td align=\"right\" port=\"i1\">YARP timed transform</td></tr>\n\
907  <tr><td align=\"right\" port=\"i2\">YARP static transform</td></tr>\n\
908  </table>>]\n\
909  key2[label=<<table border=\"0\" cellpadding=\"2\" cellspacing=\"0\" cellborder=\"0\">\n\
910  <tr><td port = \"i1\">&nbsp;</td></tr>\n\
911  <tr><td port = \"i2\">&nbsp;</td></tr>\n\
912  </table>>]\n\
913  key:i1:e -> key2:i1:w [color = blue]\n\
914  key:i2:e -> key2:i2:w [color = blue, style=dashed]\n\
915  } }";
916 
917  std::string command_string = "printf '" + dot_string + legend + "' | dot -Tpdf > frames.pdf";
918 #if defined (__linux__)
919  int ret = std::system("dot -V");
920  if (ret != 0)
921  {
922  yCError(FRAMETRANSFORMCLIENT) << "dot executable not found. Please install graphviz.";
923  return false;
924  }
925 
926  yCDebug(FRAMETRANSFORMCLIENT) << "Command string is:" << command_string;
927  ret = std::system(command_string.c_str());
928  if (ret != 0)
929  {
930  yCError(FRAMETRANSFORMCLIENT) << "The following command failed to execute:" << command_string;
931  return false;
932  }
933 #else
934  yCError(FRAMETRANSFORMCLIENT) << "Not yet implemented. Available only Linux";
935  return false;
936 #endif
937  return true;
938 }
float t
#define LOG_THROTTLE_PERIOD
CMRC_DECLARE(frameTransformRC)
constexpr yarp::conf::vocab32_t VOCAB_ERR
Definition: GenericVocabs.h:17
bool ret
yarp::dev::IFrameTransformStorageUtils * m_ift_util
bool waitForTransform(const std::string &target_frame_id, const std::string &source_frame_id, const double &timeout) override
Block until a transform from source_frame_id to target_frame_id is possible or it times out.
bool getAllFrameIds(std::vector< std::string > &ids) override
Gets a vector containing all the registered frames.
bool allFramesAsString(std::string &all_frames) override
Creates a debug string containing the list of all registered frames.
std::string priv_get_matrix_as_text(yarp::math::FrameTransform *t)
bool getParent(const std::string &frame_id, std::string &parent_frame_id) override
Get the parent of a frame.
void threadRelease() override
Release method.
show_transforms_in_diagram_t m_show_transforms_in_diagram
void run() override
Loop function.
bool deleteTransform(const std::string &target_frame_id, const std::string &source_frame_id) override
Deletes a transform between two frames.
bool open(yarp::os::Searchable &config) override
Open the DeviceDriver.
bool close() override
Close the DeviceDriver.
bool transformQuaternion(const std::string &target_frame_id, const std::string &source_frame_id, const yarp::math::Quaternion &input_quaternion, yarp::math::Quaternion &transformed_quaternion) override
Transform a quaternion into the target frame.
bool transformPose(const std::string &target_frame_id, const std::string &source_frame_id, const yarp::sig::Vector &input_pose, yarp::sig::Vector &transformed_pose) override
Transform a Stamped Pose into the target frame.
bool clear() override
Removes all the registered transforms.
std::vector< broadcast_port_t * > m_array_of_ports
bool read(yarp::os::ConnectionReader &connection) override
Read this object from a network connection.
bool threadInit() override
Initialization method.
bool transformPoint(const std::string &target_frame_id, const std::string &source_frame_id, const yarp::sig::Vector &input_point, yarp::sig::Vector &transformed_point) override
Transform a point into the target frame.
bool setTransformStatic(const std::string &target_frame_id, const std::string &source_frame_id, const yarp::sig::Matrix &transform) override
Register a static transform between two frames.
bool frameExists(const std::string &frame_id) override
Check if a frame exists.
bool setTransform(const std::string &target_frame_id, const std::string &source_frame_id, const yarp::sig::Matrix &transform) override
Register a transform between two frames.
bool canTransform(const std::string &target_frame, const std::string &source_frame) override
Test if a transform exists.
bool getTransform(const std::string &target_frame_id, const std::string &source_frame_id, yarp::sig::Matrix &transform) override
Get the transform between two frames.
FrameTransformContainer: A class that contains a vector of frame transformations and exposes yarp::de...
std::recursive_mutex m_trf_mutex
virtual bool getInternalContainer(FrameTransformContainer *&container)=0
void fromRotationMatrix(const yarp::sig::Matrix &R)
Converts a rotation matrix to a quaternion.
Definition: Quaternion.cpp:162
A simple collection of objects that can be described and transmitted in a portable way.
Definition: Bottle.h:74
void addVocab32(yarp::conf::vocab32_t x)
Places a vocabulary item in the bottle, at the end of the list.
Definition: Bottle.cpp:164
size_type size() const
Gets the number of elements in the bottle.
Definition: Bottle.cpp:251
bool read(ConnectionReader &reader) override
Set the bottle's value based on input from a network connection.
Definition: Bottle.cpp:240
Value & get(size_type index) const
Reads a Value v from a certain part of the list.
Definition: Bottle.cpp:246
void clear()
Empties the bottle of any objects it contains.
Definition: Bottle.cpp:121
bool write(ConnectionWriter &writer) const override
Output a representation of the bottle to a network connection.
Definition: Bottle.cpp:230
void addString(const char *str)
Places a string in the bottle, at the end of the list.
Definition: Bottle.cpp:170
An interface for reading from a network connection.
virtual ConnectionWriter * getWriter()=0
Gets a way to reply to the message, if possible.
An interface for writing to a network connection.
static bool exists(const std::string &port, bool quiet=true, bool checkVer=true)
Check for a port to be ready and responsive.
Definition: Network.cpp:746
An abstraction for a periodic thread.
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
void fromString(const std::string &txt, bool wipe=true)
Interprets a string as a list of properties.
Definition: Property.cpp:1063
bool check(const std::string &key) const override
Check if there exists a property of the given name.
Definition: Property.cpp:1041
void unput(const std::string &key)
Remove the association from the given key to a value, if present.
Definition: Property.cpp:1046
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.
static double nowSystem()
Definition: SystemClock.cpp:34
static void delaySystem(double seconds)
Definition: SystemClock.cpp:29
virtual yarp::conf::float64_t asFloat64() const
Get 64-bit floating point value.
Definition: Value.cpp:222
std::string toString() const override
Return a standard text representation of the content of the object.
Definition: Value.cpp:356
virtual std::string asString() const
Get string value.
Definition: Value.cpp:234
Result of the parsing of XMLReader.
Definition: XMLReader.h:25
bool parsingIsSuccessful
True if the parsing was successful, false otherwise.
Definition: XMLReader.h:37
Robot robot
If parsingIsSuccessful is true, contains a valid robot instance.
Definition: XMLReader.h:42
XMLReaderResult getRobotFromString(const std::string &filename, const yarp::os::Searchable &config=yarp::os::Property())
Parse the XML description of a robotinterface from a string.
Definition: XMLReader.cpp:140
A class for a Matrix.
Definition: Matrix.h:43
std::string toString(int precision=-1, int width=-1, const char *endRowStr="\n") const
Print matrix to a string.
Definition: Matrix.cpp:171
const Matrix & eye()
Build an identity matrix, don't resize.
Definition: Matrix.cpp:456
void resize(size_t size) override
Resize the vector.
Definition: Vector.h:222
size_t size() const
Definition: Vector.h:323
void push_back(const T &elem)
Push a new element in the vector: size is changed.
Definition: Vector.h:250
void pop_back()
Pop an element out of the vector: size is changed.
Definition: Vector.h:278
#define M_PI
#define yCInfo(component,...)
Definition: LogComponent.h:132
#define yCError(component,...)
Definition: LogComponent.h:154
#define yCAssert(component, x)
Definition: LogComponent.h:169
#define yCTrace(component,...)
Definition: LogComponent.h:85
#define yCWarning(component,...)
Definition: LogComponent.h:143
#define yCDebug(component,...)
Definition: LogComponent.h:109
#define YARP_LOG_COMPONENT(name,...)
Definition: LogComponent.h:77
#define yCErrorThrottle(component, period,...)
Definition: LogComponent.h:157
Definition: FrameGraph.h:19
std::string to_string(IntegerType x)
Definition: numeric.h:115
An interface for the device drivers.
yarp::sig::Matrix SE3inv(const yarp::sig::Matrix &H)
Returns the inverse of a 4 by 4 rototranslational matrix (defined in Math.h).
Definition: math.cpp:912
double now()
Return the current time in seconds, relative to an arbitrary starting point.
Definition: Time.cpp:121
An interface to the operating system, including Port based communication.
Signal processing.
Definition: Image.h:22