test_rtde_writer.cpp
Go to the documentation of this file.
1 // -- BEGIN LICENSE BLOCK ----------------------------------------------
2 // Copyright 2022 Universal Robots A/S
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are met:
6 //
7 // * Redistributions of source code must retain the above copyright
8 // notice, this list of conditions and the following disclaimer.
9 //
10 // * Redistributions in binary form must reproduce the above copyright
11 // notice, this list of conditions and the following disclaimer in the
12 // documentation and/or other materials provided with the distribution.
13 //
14 // * Neither the name of the {copyright_holder} nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 // POSSIBILITY OF SUCH DAMAGE.
29 // -- END LICENSE BLOCK ------------------------------------------------
30 
31 #include <gtest/gtest.h>
32 #include <condition_variable>
33 
37 
38 using namespace urcl;
39 
40 class RTDEWriterTest : public ::testing::Test
41 {
42 protected:
43  using input_types = std::variant<uint8_t, bool, uint32_t, int32_t, double>;
44 
45  void SetUp()
46  {
47  // The port shouldn't collide with any of the ports the robot is using
48  server_.reset(new comm::TCPServer(60004));
49  server_->setMessageCallback(std::bind(&RTDEWriterTest::messageCallback, this, std::placeholders::_1,
50  std::placeholders::_2, std::placeholders::_3));
51  server_->start();
52 
53  stream_.reset(new comm::URStream<rtde_interface::RTDEPackage>("127.0.0.1", 60004));
54  stream_->connect();
55 
56  writer_.reset(new rtde_interface::RTDEWriter(stream_.get(), input_recipe_));
57  writer_->init(1);
58  }
59 
60  void TearDown()
61  {
62  // Clean up
63  writer_.reset();
64  stream_.reset();
65  server_.reset();
66  }
67 
68  void messageCallback(const int filedescriptor, char* buffer, int nbytesrecv)
69  {
70  std::lock_guard<std::mutex> lk(message_mutex_);
71  uint8_t* buf = reinterpret_cast<uint8_t*>(buffer);
72  comm::BinParser bp(buf, nbytesrecv);
73  // These might be needed in the test
74  uint16_t size;
75  uint8_t type, recipe_id;
76  bp.parse(size);
77  bp.parse(type);
78  bp.parse(recipe_id);
79  parseMessage(bp);
80  message_cv_.notify_one();
81  message_callback_ = true;
82  }
83 
84  bool waitForMessageCallback(int milliseconds = 100)
85  {
86  std::unique_lock<std::mutex> lk(message_mutex_);
87  if (message_cv_.wait_for(lk, std::chrono::milliseconds(milliseconds)) == std::cv_status::no_timeout ||
88  message_callback_ == true)
89  {
90  message_callback_ = false;
91  return true;
92  }
93  else
94  {
95  return false;
96  }
97  }
98 
99  // Helper function to see if data field exists in the parsed message
100  bool dataFieldExist(std::string name)
101  {
102  if (parsed_data_.find(name) != parsed_data_.end())
103  {
104  return true;
105  }
106  std::cout << "Failed to find data field " << name << " this should not happen! Have a look at the test case"
107  << std::endl;
108  return false;
109  }
110 
111  std::vector<std::string> input_recipe_ = {
112  "speed_slider_mask",
113  "speed_slider_fraction",
114  "standard_digital_output_mask",
115  "standard_digital_output",
116  "configurable_digital_output_mask",
117  "configurable_digital_output",
118  "tool_digital_output_mask",
119  "tool_digital_output",
120  "standard_analog_output_mask",
121  "standard_analog_output_type",
122  "standard_analog_output_0",
123  "standard_analog_output_1",
124  "input_bit_register_65",
125  "input_int_register_25",
126  "input_double_register_25",
127  };
128  std::unique_ptr<rtde_interface::RTDEWriter> writer_;
129  std::unique_ptr<comm::TCPServer> server_;
130  std::unique_ptr<comm::URStream<rtde_interface::RTDEPackage>> stream_;
131  std::unordered_map<std::string, input_types> parsed_data_;
132 
133 private:
135  {
136  for (auto& item : input_recipe_)
137  {
138  if (input_map_types_.find(item) != input_map_types_.end())
139  {
140  input_types entry = input_map_types_[item];
141  std::visit([&bp](auto&& arg) { bp.parse(arg); }, entry);
142  parsed_data_[item] = entry;
143  }
144  }
145  }
146 
147  std::condition_variable message_cv_;
148  std::mutex message_mutex_;
149  bool message_callback_ = false;
150 
151  std::unordered_map<std::string, input_types> input_map_types_ = {
152  { "speed_slider_mask", uint32_t() },
153  { "speed_slider_fraction", double() },
154  { "standard_digital_output_mask", uint8_t() },
155  { "standard_digital_output", uint8_t() },
156  { "configurable_digital_output_mask", uint8_t() },
157  { "configurable_digital_output", uint8_t() },
158  { "tool_digital_output_mask", uint8_t() },
159  { "tool_digital_output", uint8_t() },
160  { "standard_analog_output_mask", uint8_t() },
161  { "standard_analog_output_type", uint8_t() },
162  { "standard_analog_output_0", double() },
163  { "standard_analog_output_1", double() },
164  { "input_bit_register_65", bool() },
165  { "input_int_register_25", int32_t() },
166  { "input_double_register_25", double() },
167  };
168 };
169 
170 // Use other port and create test fixture
171 TEST_F(RTDEWriterTest, send_speed_slider)
172 {
173  uint32_t expected_speed_slider_mask = 1;
174  double send_speed_slider_fraction = 0.5;
175 
176  EXPECT_TRUE(writer_->sendSpeedSlider(send_speed_slider_fraction));
177 
178  waitForMessageCallback(1000);
179 
180  ASSERT_TRUE(dataFieldExist("speed_slider_fraction"));
181  ASSERT_TRUE(dataFieldExist("speed_slider_mask"));
182 
183  double received_speed_slider_fraction = std::get<double>(parsed_data_["speed_slider_fraction"]);
184  uint32_t received_speed_slider_mask = std::get<uint32_t>(parsed_data_["speed_slider_mask"]);
185 
186  EXPECT_EQ(send_speed_slider_fraction, received_speed_slider_fraction);
187  EXPECT_EQ(expected_speed_slider_mask, received_speed_slider_mask);
188 
189  // Setting speed slider fraction below 0 or above 1, should return false
190  EXPECT_FALSE(writer_->sendSpeedSlider(-1));
191  EXPECT_FALSE(writer_->sendSpeedSlider(2));
192 }
193 
194 TEST_F(RTDEWriterTest, send_standard_digital_output)
195 {
196  uint8_t expected_standard_digital_output_mask = 4;
197  uint8_t pin = 2;
198  bool send_pin_value = true;
199  EXPECT_TRUE(writer_->sendStandardDigitalOutput(pin, send_pin_value));
200 
201  waitForMessageCallback(1000);
202 
203  ASSERT_TRUE(dataFieldExist("standard_digital_output"));
204  ASSERT_TRUE(dataFieldExist("standard_digital_output_mask"));
205 
206  bool received_pin_value = std::get<uint8_t>(parsed_data_["standard_digital_output"]) != 0;
207  uint8_t received_standard_digital_output_mask = std::get<uint8_t>(parsed_data_["standard_digital_output_mask"]);
208 
209  EXPECT_EQ(send_pin_value, received_pin_value);
210  EXPECT_EQ(expected_standard_digital_output_mask, received_standard_digital_output_mask);
211 
212  // Changing pins above 7, should return false.
213  pin = 8;
214  EXPECT_FALSE(writer_->sendStandardDigitalOutput(pin, send_pin_value));
215 }
216 
217 TEST_F(RTDEWriterTest, send_configurable_digital_output)
218 {
219  uint8_t expected_configurable_digital_output_mask = 8;
220  uint8_t pin = 3;
221  bool send_pin_value = true;
222  EXPECT_TRUE(writer_->sendConfigurableDigitalOutput(pin, send_pin_value));
223 
224  waitForMessageCallback(1000);
225 
226  ASSERT_TRUE(dataFieldExist("configurable_digital_output"));
227  ASSERT_TRUE(dataFieldExist("configurable_digital_output_mask"));
228 
229  bool received_pin_value = std::get<uint8_t>(parsed_data_["configurable_digital_output"]) != 0;
230  uint8_t received_standard_digital_output_mask = std::get<uint8_t>(parsed_data_["configurable_digital_output_mask"]);
231 
232  EXPECT_EQ(send_pin_value, received_pin_value);
233  EXPECT_EQ(expected_configurable_digital_output_mask, received_standard_digital_output_mask);
234 
235  // Changing pins above 7, should return false.
236  pin = 8;
237  EXPECT_FALSE(writer_->sendStandardDigitalOutput(pin, send_pin_value));
238 }
239 
240 TEST_F(RTDEWriterTest, send_tool_digital_output)
241 {
242  uint8_t expected_tool_digital_output_mask = 1;
243  uint8_t pin = 0;
244  bool send_pin_value = true;
245  EXPECT_TRUE(writer_->sendToolDigitalOutput(pin, send_pin_value));
246 
247  waitForMessageCallback(1000);
248 
249  ASSERT_TRUE(dataFieldExist("tool_digital_output"));
250  ASSERT_TRUE(dataFieldExist("tool_digital_output_mask"));
251 
252  bool received_pin_value = std::get<uint8_t>(parsed_data_["tool_digital_output"]) != 0;
253  uint8_t received_tool_digital_output_mask = std::get<uint8_t>(parsed_data_["tool_digital_output_mask"]);
254 
255  EXPECT_EQ(send_pin_value, received_pin_value);
256  EXPECT_EQ(expected_tool_digital_output_mask, received_tool_digital_output_mask);
257 
258  // Changing pins above 1, should return false.
259  pin = 2;
260  EXPECT_FALSE(writer_->sendToolDigitalOutput(pin, send_pin_value));
261 }
262 
263 TEST_F(RTDEWriterTest, send_standard_analog_output)
264 {
265  uint8_t expected_standard_analog_output_mask = 1;
266  uint8_t pin = 0;
267  double send_analog_output = 1;
268  EXPECT_TRUE(writer_->sendStandardAnalogOutput(pin, send_analog_output));
269 
270  waitForMessageCallback(1000);
271 
272  ASSERT_TRUE(dataFieldExist("standard_analog_output_0"));
273  ASSERT_TRUE(dataFieldExist("standard_analog_output_1"));
274  ASSERT_TRUE(dataFieldExist("standard_analog_output_mask"));
275 
276  double received_analog_output = std::get<double>(parsed_data_["standard_analog_output_0"]);
277  uint8_t received_standard_analog_output_mask = std::get<uint8_t>(parsed_data_["standard_analog_output_mask"]);
278 
279  EXPECT_EQ(send_analog_output, received_analog_output);
280  EXPECT_EQ(expected_standard_analog_output_mask, received_standard_analog_output_mask);
281 
282  pin = 1;
283  expected_standard_analog_output_mask = 2;
284  EXPECT_TRUE(writer_->sendStandardAnalogOutput(pin, send_analog_output));
285 
286  waitForMessageCallback(1000);
287 
288  received_analog_output = std::get<double>(parsed_data_["standard_analog_output_1"]);
289  received_standard_analog_output_mask = std::get<uint8_t>(parsed_data_["standard_analog_output_mask"]);
290 
291  EXPECT_EQ(send_analog_output, received_analog_output);
292  EXPECT_EQ(expected_standard_analog_output_mask, received_standard_analog_output_mask);
293 
294  // Changing pins above 1, should return false.
295  pin = 2;
296  EXPECT_FALSE(writer_->sendStandardAnalogOutput(pin, send_analog_output));
297 
298  // Setting analog output below 0 or above 1, should return false
299  pin = 1;
300  EXPECT_FALSE(writer_->sendStandardAnalogOutput(pin, 1.1));
301  EXPECT_FALSE(writer_->sendStandardAnalogOutput(pin, -0.1));
302 }
303 
304 TEST_F(RTDEWriterTest, send_input_bit_register)
305 {
306  uint32_t register_id = 65;
307  bool send_register_value = true;
308  EXPECT_TRUE(writer_->sendInputBitRegister(register_id, send_register_value));
309 
310  waitForMessageCallback(1000);
311 
312  ASSERT_TRUE(dataFieldExist("input_bit_register_65"));
313 
314  bool received_register_value = std::get<bool>(parsed_data_["input_bit_register_65"]);
315 
316  EXPECT_EQ(send_register_value, received_register_value);
317 
318  // Changing registers below 64 and above 127, should return false.
319  register_id = 63;
320  EXPECT_FALSE(writer_->sendInputBitRegister(register_id, send_register_value));
321  register_id = 128;
322  EXPECT_FALSE(writer_->sendInputBitRegister(register_id, send_register_value));
323 }
324 
325 TEST_F(RTDEWriterTest, send_input_int_register)
326 {
327  uint32_t register_id = 25;
328  int32_t send_register_value = 21;
329  EXPECT_TRUE(writer_->sendInputIntRegister(register_id, send_register_value));
330 
331  waitForMessageCallback(1000);
332 
333  ASSERT_TRUE(dataFieldExist("input_int_register_25"));
334 
335  int32_t received_register_value = std::get<int32_t>(parsed_data_["input_int_register_25"]);
336 
337  EXPECT_EQ(send_register_value, received_register_value);
338 
339  // Changing registers below 23 and above 48, should return false.
340  register_id = 23;
341  EXPECT_FALSE(writer_->sendInputIntRegister(register_id, send_register_value));
342  register_id = 48;
343  EXPECT_FALSE(writer_->sendInputIntRegister(register_id, send_register_value));
344 }
345 
346 TEST_F(RTDEWriterTest, send_input_double_register)
347 {
348  uint32_t register_id = 25;
349  double send_register_value = 2.1;
350  EXPECT_TRUE(writer_->sendInputDoubleRegister(register_id, send_register_value));
351 
352  waitForMessageCallback(1000);
353 
354  ASSERT_TRUE(dataFieldExist("input_double_register_25"));
355 
356  double received_register_value = std::get<double>(parsed_data_["input_double_register_25"]);
357 
358  EXPECT_EQ(send_register_value, received_register_value);
359 
360  // Changing registers below 23 and above 48, should return false.
361  register_id = 23;
362  EXPECT_FALSE(writer_->sendInputDoubleRegister(register_id, send_register_value));
363  register_id = 48;
364  EXPECT_FALSE(writer_->sendInputDoubleRegister(register_id, send_register_value));
365 }
366 
367 int main(int argc, char* argv[])
368 {
369  ::testing::InitGoogleTest(&argc, argv);
370 
371  return RUN_ALL_TESTS();
372 }
void parse(T &val)
Parses the next bytes as given type.
Definition: bin_parser.h:139
void messageCallback(const int filedescriptor, char *buffer, int nbytesrecv)
The BinParser class handles a byte buffer and functionality to iteratively parse the content...
Definition: bin_parser.h:44
bool waitForMessageCallback(int milliseconds=100)
The stream is an abstraction of the TCPSocket that offers reading a full UR data package out of the s...
Definition: stream.h:42
std::unordered_map< std::string, input_types > parsed_data_
Wrapper class for a TCP socket server.
Definition: tcp_server.h:59
bool dataFieldExist(std::string name)
The RTDEWriter class offers an abstraction layer to send data to the robot via the RTDE interface...
Definition: rtde_writer.h:49
void parseMessage(comm::BinParser bp)
std::mutex message_mutex_
std::condition_variable message_cv_
int main(int argc, char *argv[])
std::unique_ptr< comm::URStream< rtde_interface::RTDEPackage > > stream_
TEST_F(RTDEWriterTest, send_speed_slider)
std::unique_ptr< comm::TCPServer > server_
std::unique_ptr< rtde_interface::RTDEWriter > writer_
std::variant< uint8_t, bool, uint32_t, int32_t, double > input_types


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