src/rtde/rtde_client.cpp
Go to the documentation of this file.
1 // this is for emacs file handling -*- mode: c++; indent-tabs-mode: nil -*-
2 
3 // -- BEGIN LICENSE BLOCK ----------------------------------------------
4 // Copyright 2019 FZI Forschungszentrum Informatik
5 // Created on behalf of Universal Robots A/S
6 //
7 // Licensed under the Apache License, Version 2.0 (the "License");
8 // you may not use this file except in compliance with the License.
9 // You may obtain a copy of the License at
10 //
11 // http://www.apache.org/licenses/LICENSE-2.0
12 //
13 // Unless required by applicable law or agreed to in writing, software
14 // distributed under the License is distributed on an "AS IS" BASIS,
15 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 // See the License for the specific language governing permissions and
17 // limitations under the License.
18 // -- END LICENSE BLOCK ------------------------------------------------
19 
20 //----------------------------------------------------------------------
27 //----------------------------------------------------------------------
28 
31 #include <algorithm>
32 
33 namespace urcl
34 {
35 namespace rtde_interface
36 {
37 RTDEClient::RTDEClient(std::string robot_ip, comm::INotifier& notifier, const std::string& output_recipe_file,
38  const std::string& input_recipe_file, double target_frequency)
39  : stream_(robot_ip, UR_RTDE_PORT)
40  , output_recipe_(readRecipe(output_recipe_file))
41  , input_recipe_(readRecipe(input_recipe_file))
42  , parser_(output_recipe_)
43  , prod_(stream_, parser_)
44  , pipeline_(prod_, PIPELINE_NAME, notifier, true)
45  , writer_(&stream_, input_recipe_)
46  , max_frequency_(URE_MAX_FREQUENCY)
47  , target_frequency_(target_frequency)
48  , client_state_(ClientState::UNINITIALIZED)
49 {
50 }
51 
53 {
54  disconnect();
55 }
56 
58 {
60  {
61  return true;
62  }
63 
64  unsigned int attempts = 0;
65  while (attempts < MAX_INITIALIZE_ATTEMPTS)
66  {
69  return true;
70 
71  URCL_LOG_ERROR("Failed to initialize RTDE client, retrying in 10 seconds");
72  std::this_thread::sleep_for(std::chrono::seconds(10));
73  attempts++;
74  }
75  std::stringstream ss;
76  ss << "Failed to initialize RTDE client after " << MAX_INITIALIZE_ATTEMPTS << " attempts";
77  throw UrException(ss.str());
78 }
79 
81 {
83  // A running pipeline is needed inside setup
84  pipeline_.init();
85  pipeline_.run();
86 
87  uint16_t protocol_version = MAX_RTDE_PROTOCOL_VERSION;
89  {
90  URCL_LOG_INFO("Robot did not accept RTDE protocol version '%hu'. Trying lower protocol version", protocol_version);
91  protocol_version--;
92  if (protocol_version == 0)
93  {
94  throw UrException("Protocol version for RTDE communication could not be established. Robot didn't accept any of "
95  "the suggested versions.");
96  }
97  }
99  return;
100 
101  URCL_LOG_INFO("Negotiated RTDE protocol version to %hu.", protocol_version);
102  parser_.setProtocolVersion(protocol_version);
103 
106  return;
107 
108  if (urcontrol_version_.major < 5)
109  {
111  }
112 
113  if (target_frequency_ == 0)
114  {
115  // Default to maximum frequency
117  }
118  else if (target_frequency_ <= 0.0 || target_frequency_ > max_frequency_)
119  {
120  // Target frequency outside valid range
121  throw UrException("Invalid target frequency of RTDE connection");
122  }
123 
124  setupOutputs(protocol_version);
126  return;
127 
128  if (!isRobotBooted())
129  {
130  disconnect();
131  return;
132  }
133 
134  setupInputs();
136  return;
137 
138  // We finished communication for now
139  pipeline_.stop();
141 }
142 
143 bool RTDEClient::negotiateProtocolVersion(const uint16_t protocol_version)
144 {
145  // Protocol version should always be 1 before starting negotiation
147  unsigned int num_retries = 0;
148  uint8_t buffer[4096];
149  size_t size;
150  size_t written;
151  size = RequestProtocolVersionRequest::generateSerializedRequest(buffer, protocol_version);
152  if (!stream_.write(buffer, size, written))
153  {
154  URCL_LOG_ERROR("Sending protocol version query to robot failed, disconnecting");
155  disconnect();
156  return false;
157  }
158 
159  while (num_retries < MAX_REQUEST_RETRIES)
160  {
161  std::unique_ptr<RTDEPackage> package;
162  if (!pipeline_.getLatestProduct(package, std::chrono::milliseconds(1000)))
163  {
164  URCL_LOG_ERROR("failed to get package from rtde interface, disconnecting");
165  disconnect();
166  return false;
167  }
168  if (rtde_interface::RequestProtocolVersion* tmp_version =
169  dynamic_cast<rtde_interface::RequestProtocolVersion*>(package.get()))
170  {
171  // Reset the num_tries variable in case we have to try with another protocol version.
172  num_retries = 0;
173  return tmp_version->accepted_;
174  }
175  else
176  {
177  std::stringstream ss;
178  ss << "Did not receive protocol negotiation answer from robot. Message received instead: " << std::endl
179  << package->toString() << ". Retrying...";
180  num_retries++;
181  URCL_LOG_WARN("%s", ss.str().c_str());
182  }
183  }
184  std::stringstream ss;
185  ss << "Could not negotiate RTDE protocol version after " << MAX_REQUEST_RETRIES
186  << " tries. Please check the output of the "
187  "negotiation attempts above to get a hint what could be wrong.";
188  throw UrException(ss.str());
189 }
190 
192 {
193  unsigned int num_retries = 0;
194  uint8_t buffer[4096];
195  size_t size;
196  size_t written;
198  if (!stream_.write(buffer, size, written))
199  {
200  URCL_LOG_ERROR("Sending urcontrol version query request to robot failed, disconnecting");
201  disconnect();
202  return;
203  }
204 
205  std::unique_ptr<RTDEPackage> package;
206  while (num_retries < MAX_REQUEST_RETRIES)
207  {
208  if (!pipeline_.getLatestProduct(package, std::chrono::milliseconds(1000)))
209  {
210  URCL_LOG_ERROR("No answer to urcontrol version query was received from robot, disconnecting");
211  disconnect();
212  return;
213  }
214 
215  if (rtde_interface::GetUrcontrolVersion* tmp_urcontrol_version =
216  dynamic_cast<rtde_interface::GetUrcontrolVersion*>(package.get()))
217  {
218  urcontrol_version_ = tmp_urcontrol_version->version_information_;
219  return;
220  }
221  else
222  {
223  std::stringstream ss;
224  ss << "Did not receive protocol negotiation answer from robot. Message received instead: " << std::endl
225  << package->toString() << ". Retrying...";
226  num_retries++;
227  URCL_LOG_WARN("%s", ss.str().c_str());
228  }
229  }
230  std::stringstream ss;
231  ss << "Could not query urcontrol version after " << MAX_REQUEST_RETRIES
232  << " tries. Please check the output of the "
233  "negotiation attempts above to get a hint what could be wrong.";
234  throw UrException(ss.str());
235 }
236 
237 void RTDEClient::setupOutputs(const uint16_t protocol_version)
238 {
239  unsigned int num_retries = 0;
240  size_t size;
241  size_t written;
242  uint8_t buffer[4096];
243  URCL_LOG_INFO("Setting up RTDE communication with frequency %f", target_frequency_);
244  // Add timestamp to rtde output recipe, used to check if robot is booted
245  const std::string timestamp = "timestamp";
246  auto it = std::find(output_recipe_.begin(), output_recipe_.end(), timestamp);
247  if (it == output_recipe_.end())
248  {
249  output_recipe_.push_back(timestamp);
250  }
251  if (protocol_version == 2)
252  {
254  }
255  else
256  {
258  {
259  URCL_LOG_WARN("It is not possible to set a target frequency when using protocol version 1. A frequency "
260  "equivalent to the maximum frequency will be used instead.");
261  }
263  }
264 
265  // Send output recipe to robot
266  if (!stream_.write(buffer, size, written))
267  {
268  URCL_LOG_ERROR("Could not send RTDE output recipe to robot, disconnecting");
269  disconnect();
270  return;
271  }
272 
273  while (num_retries < MAX_REQUEST_RETRIES)
274  {
275  std::unique_ptr<RTDEPackage> package;
276  if (!pipeline_.getLatestProduct(package, std::chrono::milliseconds(1000)))
277  {
278  URCL_LOG_ERROR("Did not receive confirmation on RTDE output recipe, disconnecting");
279  disconnect();
280  return;
281  }
282 
284  dynamic_cast<rtde_interface::ControlPackageSetupOutputs*>(package.get()))
285 
286  {
287  std::vector<std::string> variable_types = splitVariableTypes(tmp_output->variable_types_);
288  assert(output_recipe_.size() == variable_types.size());
289  for (std::size_t i = 0; i < variable_types.size(); ++i)
290  {
291  URCL_LOG_DEBUG("%s confirmed as datatype: %s", output_recipe_[i].c_str(), variable_types[i].c_str());
292  if (variable_types[i] == "NOT_FOUND")
293  {
294  std::string message = "Variable '" + output_recipe_[i] +
295  "' not recognized by the robot. Probably your output recipe contains errors";
296  throw UrException(message);
297  }
298  }
299  return;
300  }
301  else
302  {
303  std::stringstream ss;
304  ss << "Did not receive answer to RTDE output setup. Message received instead: " << std::endl
305  << package->toString() << ". Retrying...";
306  num_retries++;
307  URCL_LOG_WARN("%s", ss.str().c_str());
308  }
309  }
310  std::stringstream ss;
311  ss << "Could not setup RTDE outputs after " << MAX_REQUEST_RETRIES
312  << " tries. Please check the output of the "
313  "negotiation attempts above to get a hint what could be wrong.";
314  throw UrException(ss.str());
315 }
316 
318 {
319  unsigned int num_retries = 0;
320  size_t size;
321  size_t written;
322  uint8_t buffer[4096];
324  if (!stream_.write(buffer, size, written))
325  {
326  URCL_LOG_ERROR("Could not send RTDE input recipe to robot, disconnecting");
327  disconnect();
328  return;
329  }
330 
331  while (num_retries < MAX_REQUEST_RETRIES)
332  {
333  std::unique_ptr<RTDEPackage> package;
334  if (!pipeline_.getLatestProduct(package, std::chrono::milliseconds(1000)))
335  {
336  URCL_LOG_ERROR("Did not receive confirmation on RTDE input recipe, disconnecting");
337  disconnect();
338  return;
339  }
340 
342  dynamic_cast<rtde_interface::ControlPackageSetupInputs*>(package.get()))
343 
344  {
345  std::vector<std::string> variable_types = splitVariableTypes(tmp_input->variable_types_);
346  assert(input_recipe_.size() == variable_types.size());
347  for (std::size_t i = 0; i < variable_types.size(); ++i)
348  {
349  URCL_LOG_DEBUG("%s confirmed as datatype: %s", input_recipe_[i].c_str(), variable_types[i].c_str());
350  if (variable_types[i] == "NOT_FOUND")
351  {
352  std::string message = "Variable '" + input_recipe_[i] +
353  "' not recognized by the robot. Probably your input recipe contains errors";
354  throw UrException(message);
355  }
356  else if (variable_types[i] == "IN_USE")
357  {
358  std::string message = "Variable '" + input_recipe_[i] +
359  "' is currently controlled by another RTDE client. The input recipe can't be used as "
360  "configured";
361  throw UrException(message);
362  }
363  }
364  writer_.init(tmp_input->input_recipe_id_);
365 
366  return;
367  }
368  else
369  {
370  std::stringstream ss;
371  ss << "Did not receive answer to RTDE input setup. Message received instead: " << std::endl
372  << package->toString() << ". Retrying...";
373  num_retries++;
374  URCL_LOG_WARN("%s", ss.str().c_str());
375  }
376  }
377  std::stringstream ss;
378  ss << "Could not setup RTDE inputs after " << MAX_REQUEST_RETRIES
379  << " tries. Please check the output of the "
380  "negotiation attempts above to get a hint what could be wrong.";
381  throw UrException(ss.str());
382 }
383 
385 {
386  // If communication is started it should be paused before disconnecting
387  sendPause();
388  pipeline_.stop();
389  stream_.disconnect();
391 }
392 
394 {
395  // We need to trigger the robot to start sending RTDE data packages in the negotiated format, in order to read
396  // the time since the controller was started.
397  if (!sendStart())
398  return false;
399 
400  std::unique_ptr<RTDEPackage> package;
401  double timestamp = 0;
402  int reading_count = 0;
403  // During bootup the RTDE interface gets restarted once. If we connect to the RTDE interface before that happens, we
404  // might end up in a situation where the RTDE connection is in an invalid state.
405  // It should be fine if we manage to read from the RTDE interface for at least one second or if the robot has been up
406  // for more then 40 seconds (During the reset the timestamp will also be reset to 0).
407  // TODO (anyone): Find a better solution to check for a proper connection.
408 
409  while (timestamp < 40 && reading_count < target_frequency_ * 2)
410  {
411  // Set timeout based on target frequency, to make sure that reading doesn't timeout
412  int timeout = static_cast<int>((1 / target_frequency_) * 1000) * 10;
413  if (pipeline_.getLatestProduct(package, std::chrono::milliseconds(timeout)))
414  {
415  rtde_interface::DataPackage* tmp_input = dynamic_cast<rtde_interface::DataPackage*>(package.get());
416  tmp_input->getData("timestamp", timestamp);
417  reading_count++;
418  }
419  else
420  {
421  return false;
422  }
423  }
424 
425  // Pause connection again
426  if (!sendPause())
427  return false;
428 
429  return true;
430 }
431 
433 {
435  return true;
436 
438  {
439  URCL_LOG_ERROR("Cannot start an unitialized client, please initialize it first");
440  return false;
441  }
442 
443  pipeline_.run();
444 
445  if (sendStart())
446  {
448  return true;
449  }
450  else
451  {
452  return false;
453  }
454 }
455 
457 {
459  return true;
461  {
462  URCL_LOG_ERROR("Can't pause the client, as it hasn't been started");
463  return false;
464  }
465 
466  if (sendPause())
467  {
469  return true;
470  }
471  else
472  {
473  return false;
474  }
475 }
476 
478 {
479  uint8_t buffer[4096];
480  size_t size;
481  size_t written;
483  if (!stream_.write(buffer, size, written))
484  {
485  URCL_LOG_ERROR("Sending RTDE start command failed!");
486  return false;
487  }
488 
489  std::unique_ptr<RTDEPackage> package;
490  unsigned int num_retries = 0;
491  while (num_retries < MAX_REQUEST_RETRIES)
492  {
493  if (!pipeline_.getLatestProduct(package, std::chrono::milliseconds(1000)))
494  {
495  URCL_LOG_ERROR("Could not get response to RTDE communication start request from robot");
496  return false;
497  }
498 
499  if (rtde_interface::ControlPackageStart* tmp = dynamic_cast<rtde_interface::ControlPackageStart*>(package.get()))
500  {
501  return tmp->accepted_;
502  }
503  else
504  {
505  std::stringstream ss;
506  ss << "Did not receive answer to RTDE start request. Message received instead: " << std::endl
507  << package->toString();
508  URCL_LOG_WARN("%s", ss.str().c_str());
509  return false;
510  }
511  }
512  std::stringstream ss;
513  ss << "Could not start RTDE communication after " << MAX_REQUEST_RETRIES
514  << " tries. Please check the output of the "
515  "negotiation attempts above to get a hint what could be wrong.";
516  throw UrException(ss.str());
517 }
518 
520 {
521  uint8_t buffer[4096];
522  size_t size;
523  size_t written;
525  if (!stream_.write(buffer, size, written))
526  {
527  URCL_LOG_ERROR("Sending RTDE pause command failed!");
528  return false;
529  }
530  std::unique_ptr<RTDEPackage> package;
531  std::chrono::time_point start = std::chrono::steady_clock::now();
532  int seconds = 5;
533  while (std::chrono::steady_clock::now() - start < std::chrono::seconds(seconds))
534  {
535  if (!pipeline_.getLatestProduct(package, std::chrono::milliseconds(1000)))
536  {
537  URCL_LOG_ERROR("Could not get response to RTDE communication pause request from robot");
538  return false;
539  }
540  if (rtde_interface::ControlPackagePause* tmp = dynamic_cast<rtde_interface::ControlPackagePause*>(package.get()))
541  {
543  return tmp->accepted_;
544  }
545  }
546  std::stringstream ss;
547  ss << "Could not receive answer to pause RTDE communication after " << seconds << " seconds.";
548  throw UrException(ss.str());
549 }
550 
551 std::vector<std::string> RTDEClient::readRecipe(const std::string& recipe_file)
552 {
553  std::vector<std::string> recipe;
554  std::ifstream file(recipe_file);
555  if (file.fail())
556  {
557  std::stringstream msg;
558  msg << "Opening file '" << recipe_file << "' failed with error: " << strerror(errno);
559  URCL_LOG_ERROR("%s", msg.str().c_str());
560  throw UrException(msg.str());
561  }
562  std::string line;
563  while (std::getline(file, line))
564  {
565  recipe.push_back(line);
566  }
567  return recipe;
568 }
569 
570 std::unique_ptr<rtde_interface::DataPackage> RTDEClient::getDataPackage(std::chrono::milliseconds timeout)
571 {
572  std::unique_ptr<RTDEPackage> urpackage;
573  if (pipeline_.getLatestProduct(urpackage, timeout))
574  {
575  rtde_interface::DataPackage* tmp = dynamic_cast<rtde_interface::DataPackage*>(urpackage.get());
576  if (tmp != nullptr)
577  {
578  urpackage.release();
579  return std::unique_ptr<rtde_interface::DataPackage>(tmp);
580  }
581  }
582  return std::unique_ptr<rtde_interface::DataPackage>(nullptr);
583 }
584 
585 std::string RTDEClient::getIP() const
586 {
587  return stream_.getIP();
588 }
589 
591 {
592  return writer_;
593 }
594 
595 std::vector<std::string> RTDEClient::splitVariableTypes(const std::string& variable_types) const
596 {
597  std::vector<std::string> result;
598  std::stringstream ss(variable_types);
599  std::string substr = "";
600  while (getline(ss, substr, ','))
601  {
602  result.push_back(substr);
603  }
604  return result;
605 }
606 } // namespace rtde_interface
607 } // namespace urcl
static const unsigned MAX_REQUEST_RETRIES
Definition: rtde_client.h:53
void setProtocolVersion(uint16_t protocol_version)
Definition: rtde_parser.h:119
static size_t generateSerializedRequest(uint8_t *buffer, double output_frequency, std::vector< std::string > variable_names)
Generates a serialized package.
void init(uint8_t recipe_id)
Starts the writer thread, which periodically clears the queue to write packages to the robot...
Definition: rtde_writer.cpp:40
std::vector< std::string > splitVariableTypes(const std::string &variable_types) const
Splits a variable_types string as reported from the robot into single variable type strings...
#define URCL_LOG_ERROR(...)
Definition: log.h:26
This class handles the robot&#39;s response to a requested start in RTDE data package communication...
RTDEWriter & getWriter()
Getter for the RTDE writer, which is used to send data via the RTDE interface to the robot...
std::string getIP() const
Returns the IP address (of the machine running this driver) used for the socket connection.
std::vector< std::string > output_recipe_
Definition: rtde_client.h:194
static constexpr const double CB3_MAX_FREQUENCY
Definition: rtde_client.h:208
std::vector< std::string > readRecipe(const std::string &recipe_file)
static size_t generateSerializedRequest(uint8_t *buffer, std::vector< std::string > variable_names)
Generates a serialized package.
bool start()
Triggers the robot to start sending RTDE data packages in the negotiated format.
bool getData(const std::string &name, T &val)
Get a data field from the DataPackage.
Definition: data_package.h:128
static size_t generateSerializedRequest(uint8_t *buffer, uint16_t version)
Generates a serialized package.
std::vector< std::string > input_recipe_
Definition: rtde_client.h:195
static const std::string PIPELINE_NAME
Definition: rtde_client.h:46
bool isRobotBooted()
Checks whether the robot is booted, this is done by looking at the timestamp from the robot controlle...
The RTDEWriter class offers an abstraction layer to send data to the robot via the RTDE interface...
Definition: rtde_writer.h:49
std::unique_ptr< rtde_interface::DataPackage > getDataPackage(std::chrono::milliseconds timeout)
Reads the pipeline to fetch the next data package.
This class handles the package detailing the UR control version sent by the robot.
static size_t generateSerializedRequest(uint8_t *buffer)
Generates a serialized package.
comm::Pipeline< RTDEPackage > pipeline_
Definition: rtde_client.h:198
Parent class for notifiers.
Definition: pipeline.h:210
static size_t generateSerializedRequest(uint8_t *buffer)
Generates a serialized package.
uint32_t major
Major version number.
#define URCL_LOG_DEBUG(...)
Definition: log.h:23
This class handles the robot&#39;s response to a requested stop in RTDE data package communication.
This class handles the robot&#39;s response to a requested output recipe setup.
static size_t generateSerializedRequest(uint8_t *buffer)
Generates a serialized package.
This class handles the robot&#39;s response after trying to set the used RTDE protocol version...
comm::URStream< RTDEPackage > stream_
Definition: rtde_client.h:193
#define URCL_LOG_WARN(...)
Definition: log.h:24
This class handles the robot&#39;s response to a requested input recipe setup.
static const unsigned MAX_INITIALIZE_ATTEMPTS
Definition: rtde_client.h:54
bool negotiateProtocolVersion(const uint16_t protocol_version)
The DataPackage class handles communication in the form of RTDE data packages both to and from the ro...
Definition: data_package.h:60
bool init()
Sets up RTDE communication with the robot. The handshake includes negotiation of the used protocol ve...
static const uint16_t MAX_RTDE_PROTOCOL_VERSION
Definition: rtde_client.h:52
#define URCL_LOG_INFO(...)
Definition: log.h:25
Our base class for exceptions. Specialized exceptions should inherit from those.
Definition: exceptions.h:41
VersionInformation urcontrol_version_
Definition: rtde_client.h:201
bool pause()
Pauses RTDE data package communication.
void setupOutputs(const uint16_t protocol_version)
static const int UR_RTDE_PORT
Definition: rtde_client.h:45


ur_client_library
Author(s): Thomas Timm Andersen, Simon Rasmussen, Felix Exner, Lea Steffen, Tristan Schnell
autogenerated on Tue Jul 4 2023 02:09:47