test_reverse_interface.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>
34 
35 using namespace urcl;
36 
37 class ReverseIntefaceTest : public ::testing::Test
38 {
39 protected:
40  class Client : public comm::TCPSocket
41  {
42  public:
43  Client(const int& port)
44  {
45  std::string host = "127.0.0.1";
46  TCPSocket::setup(host, port);
47  timeval tv;
48  tv.tv_sec = 1;
49  tv.tv_usec = 0;
50  TCPSocket::setReceiveTimeout(tv);
51  }
52 
53  void readMessage(int32_t& keep_alive_signal, vector6int32_t& pos, int32_t& control_mode)
54  {
55  // Read message
56  uint8_t buf[sizeof(int32_t) * 8];
57  uint8_t* b_pos = buf;
58  size_t read = 0;
59  size_t remainder = sizeof(int32_t) * 8;
60  while (remainder > 0)
61  {
62  if (!TCPSocket::read(b_pos, remainder, read))
63  {
64  std::cout << "Failed to read from socket, this should not happen during a test!" << std::endl;
65  break;
66  }
67  b_pos += read;
68  remainder -= read;
69  }
70 
71  // Decode keepalive signal
72  int32_t val;
73  b_pos = buf;
74  std::memcpy(&val, b_pos, sizeof(int32_t));
75  keep_alive_signal = be32toh(val);
76  b_pos += sizeof(int32_t);
77 
78  // Decode positions
79  for (unsigned int i = 0; i < pos.size(); ++i)
80  {
81  std::memcpy(&val, b_pos, sizeof(int32_t));
82  pos[i] = be32toh(val);
83  b_pos += sizeof(int32_t);
84  }
85 
86  // Decode control mode
87  std::memcpy(&val, b_pos, sizeof(int32_t));
88  control_mode = be32toh(val);
89  }
90 
91  // Helper functions to get different parts of the received message
93  {
94  int32_t keep_alive_signal;
95  int32_t control_mode;
96  vector6int32_t pos;
97  readMessage(keep_alive_signal, pos, control_mode);
98  return pos;
99  }
100 
102  {
103  int32_t keep_alive_signal;
104  int32_t control_mode;
105  vector6int32_t pos;
106  readMessage(keep_alive_signal, pos, control_mode);
107  return keep_alive_signal;
108  }
109 
110  int32_t getControlMode()
111  {
112  int32_t keep_alive_signal;
113  int32_t control_mode;
114  vector6int32_t pos;
115  readMessage(keep_alive_signal, pos, control_mode);
116  return control_mode;
117  }
118 
120  {
121  // received_pos[0]=control::TrajectoryControlMessage, when writing a trajectory control message
122  int32_t keep_alive_signal;
123  int32_t control_mode;
124  vector6int32_t pos;
125  readMessage(keep_alive_signal, pos, control_mode);
126  return pos[0];
127  }
128 
130  {
131  // received_pos[1]=point_number, when writing a trajectory control message
132  int32_t keep_alive_signal;
133  int32_t control_mode;
134  vector6int32_t pos;
135  readMessage(keep_alive_signal, pos, control_mode);
136  return pos[1];
137  }
138 
140  {
141  // received_pos[0]=control::FreedriveControlMessage, when writing a trajectory control message
142  int32_t keep_alive_signal;
143  int32_t control_mode;
144  vector6int32_t pos;
145  readMessage(keep_alive_signal, pos, control_mode);
146  return pos[0];
147  }
148 
149  protected:
150  virtual bool open(int socket_fd, struct sockaddr* address, size_t address_len)
151  {
152  return ::connect(socket_fd, address, address_len) == 0;
153  }
154  };
155 
156  void SetUp()
157  {
158  reverse_interface_.reset(new control::ReverseInterface(
159  50001, std::bind(&ReverseIntefaceTest::handleProgramState, this, std::placeholders::_1)));
160  client_.reset(new Client(50001));
161  }
162 
163  void TearDown()
164  {
165  if (client_->getState() == comm::SocketState::Connected)
166  {
167  client_->close();
168  waitForProgramState(1000, false);
169  }
170  }
171 
172  void handleProgramState(bool program_state)
173  {
174  std::lock_guard<std::mutex> lk(program_running_mutex_);
175  program_running_.notify_one();
176  program_state_ = program_state;
177  }
178 
179  bool waitForProgramState(int milliseconds = 100, bool program_state = true)
180  {
181  std::unique_lock<std::mutex> lk(program_running_mutex_);
182  if (program_running_.wait_for(lk, std::chrono::milliseconds(milliseconds)) == std::cv_status::no_timeout ||
183  program_state_ == program_state)
184  {
185  if (program_state_ == program_state)
186  {
187  return true;
188  }
189  }
190  return false;
191  }
192 
193  std::unique_ptr<control::ReverseInterface> reverse_interface_;
194  std::unique_ptr<Client> client_;
195 
196 private:
197  std::atomic<bool> program_state_ = ATOMIC_VAR_INIT(false);
198  std::condition_variable program_running_;
200 };
201 
202 TEST_F(ReverseIntefaceTest, handle_program_state)
203 {
204  // Test that handle program state is called when the client connects to the server
205  EXPECT_TRUE(waitForProgramState(1000, true));
206 
207  // Test that handle program state is called when the client disconnects from the server
208  client_->close();
209  EXPECT_TRUE(waitForProgramState(1000, false));
210 }
211 
212 TEST_F(ReverseIntefaceTest, write_positions)
213 {
214  // Wait for the client to connect to the server
215  EXPECT_TRUE(waitForProgramState(1000, true));
216 
217  urcl::vector6d_t written_positions = { 1.2, -3.1, -2.2, -3.4, 1.1, 1.2 };
218  reverse_interface_->write(&written_positions);
219  vector6int32_t received_positions = client_->getPositions();
220 
221  EXPECT_EQ(written_positions[0], ((double)received_positions[0]) / reverse_interface_->MULT_JOINTSTATE);
222  EXPECT_EQ(written_positions[1], ((double)received_positions[1]) / reverse_interface_->MULT_JOINTSTATE);
223  EXPECT_EQ(written_positions[2], ((double)received_positions[2]) / reverse_interface_->MULT_JOINTSTATE);
224  EXPECT_EQ(written_positions[3], ((double)received_positions[3]) / reverse_interface_->MULT_JOINTSTATE);
225  EXPECT_EQ(written_positions[4], ((double)received_positions[4]) / reverse_interface_->MULT_JOINTSTATE);
226  EXPECT_EQ(written_positions[5], ((double)received_positions[5]) / reverse_interface_->MULT_JOINTSTATE);
227 }
228 
229 TEST_F(ReverseIntefaceTest, write_trajectory_control_message)
230 {
231  // Wait for the client to connect to the server
232  EXPECT_TRUE(waitForProgramState(1000, true));
233 
235  reverse_interface_->writeTrajectoryControlMessage(written_control_message, 1);
236  int32_t received_control_message = client_->getTrajectoryControlMode();
237 
238  EXPECT_EQ(toUnderlying(written_control_message), received_control_message);
239 
240  written_control_message = control::TrajectoryControlMessage::TRAJECTORY_NOOP;
241  reverse_interface_->writeTrajectoryControlMessage(written_control_message, 1);
242  received_control_message = client_->getTrajectoryControlMode();
243 
244  EXPECT_EQ(toUnderlying(written_control_message), received_control_message);
245 
246  written_control_message = control::TrajectoryControlMessage::TRAJECTORY_START;
247  reverse_interface_->writeTrajectoryControlMessage(written_control_message, 1);
248  received_control_message = client_->getTrajectoryControlMode();
249 
250  EXPECT_EQ(toUnderlying(written_control_message), received_control_message);
251 }
252 
253 TEST_F(ReverseIntefaceTest, write_trajectory_point_number)
254 {
255  // Wait for the client to connect to the server
256  EXPECT_TRUE(waitForProgramState(1000, true));
257 
258  int32_t written_point_number = 2;
259  reverse_interface_->writeTrajectoryControlMessage(control::TrajectoryControlMessage::TRAJECTORY_START,
260  written_point_number);
261  int32_t received_point_number = client_->getTrajectoryPointNumber();
262 
263  EXPECT_EQ(written_point_number, received_point_number);
264 }
265 
266 TEST_F(ReverseIntefaceTest, control_mode_is_forward)
267 {
268  // Wait for the client to connect to the server
269  EXPECT_TRUE(waitForProgramState(1000, true));
270 
271  // When writing trajectory control message, the control mode should always be mode forward
272  comm::ControlMode expected_control_mode = comm::ControlMode::MODE_FORWARD;
273  reverse_interface_->writeTrajectoryControlMessage(control::TrajectoryControlMessage::TRAJECTORY_START, 1);
274  int32_t received_control_mode = client_->getControlMode();
275 
276  EXPECT_EQ(toUnderlying(expected_control_mode), received_control_mode);
277 }
278 
279 TEST_F(ReverseIntefaceTest, remaining_message_points_are_zeros)
280 {
281  // Wait for the client to connect to the server
282  EXPECT_TRUE(waitForProgramState(1000, true));
283 
284  // When using trajectory control message, the received message is keep_alive_signal=keep_alive_signal,
285  // received_pos[0]=control::TrajectoryControlMessage, received_pos[1]=point_number and received_pos[2]-received_pos[5]
286  // should be zeros.
287  reverse_interface_->writeTrajectoryControlMessage(control::TrajectoryControlMessage::TRAJECTORY_START, 1);
288  vector6int32_t received_pos = client_->getPositions();
289 
290  EXPECT_EQ(0, received_pos[2]);
291  EXPECT_EQ(0, received_pos[3]);
292  EXPECT_EQ(0, received_pos[4]);
293  EXPECT_EQ(0, received_pos[5]);
294 }
295 
296 TEST_F(ReverseIntefaceTest, keep_alive_count)
297 {
298  // Wait for the client to connect to the server
299  EXPECT_TRUE(waitForProgramState(1000, true));
300 
301  int expected_keep_alive_count = 5;
302  reverse_interface_->setKeepaliveCount(expected_keep_alive_count);
303 
304  urcl::vector6d_t pos = { 0, 0, 0, 0, 0, 0 };
305  reverse_interface_->write(&pos);
306  int32_t received_keep_alive_count = client_->getKeepAliveCount();
307 
308  EXPECT_EQ(expected_keep_alive_count, received_keep_alive_count);
309 
310  // Test that keep alive signal works with trajectory controll message as well
311  reverse_interface_->writeTrajectoryControlMessage(control::TrajectoryControlMessage::TRAJECTORY_START, 1);
312  received_keep_alive_count = client_->getKeepAliveCount();
313 
314  EXPECT_EQ(expected_keep_alive_count, received_keep_alive_count);
315 }
316 
317 TEST_F(ReverseIntefaceTest, write_control_mode)
318 {
319  // Wait for the client to connect to the server
320  EXPECT_TRUE(waitForProgramState(1000, true));
321 
322  vector6d_t pos = { 0, 0, 0, 0, 0, 0 };
323 
324  comm::ControlMode expected_control_mode = comm::ControlMode::MODE_FORWARD;
325  reverse_interface_->write(&pos, expected_control_mode);
326  int32_t received_control_mode = client_->getControlMode();
327 
328  EXPECT_EQ(toUnderlying(expected_control_mode), received_control_mode);
329 
330  expected_control_mode = comm::ControlMode::MODE_IDLE;
331  reverse_interface_->write(&pos, expected_control_mode);
332  received_control_mode = client_->getControlMode();
333 
334  EXPECT_EQ(toUnderlying(expected_control_mode), received_control_mode);
335 
336  expected_control_mode = comm::ControlMode::MODE_POSE;
337  reverse_interface_->write(&pos, expected_control_mode);
338  received_control_mode = client_->getControlMode();
339 
340  EXPECT_EQ(toUnderlying(expected_control_mode), received_control_mode);
341 
342  expected_control_mode = comm::ControlMode::MODE_SERVOJ;
343  reverse_interface_->write(&pos, expected_control_mode);
344  received_control_mode = client_->getControlMode();
345 
346  EXPECT_EQ(toUnderlying(expected_control_mode), received_control_mode);
347 
348  expected_control_mode = comm::ControlMode::MODE_SPEEDJ;
349  reverse_interface_->write(&pos, expected_control_mode);
350  received_control_mode = client_->getControlMode();
351 
352  EXPECT_EQ(toUnderlying(expected_control_mode), received_control_mode);
353 
354  expected_control_mode = comm::ControlMode::MODE_SPEEDL;
355  reverse_interface_->write(&pos, expected_control_mode);
356  received_control_mode = client_->getControlMode();
357 
358  EXPECT_EQ(toUnderlying(expected_control_mode), received_control_mode);
359 
360  expected_control_mode = comm::ControlMode::MODE_STOPPED;
361  reverse_interface_->write(&pos, expected_control_mode);
362  received_control_mode = client_->getControlMode();
363 
364  EXPECT_EQ(toUnderlying(expected_control_mode), received_control_mode);
365 
366  expected_control_mode = comm::ControlMode::MODE_UNINITIALIZED;
367  reverse_interface_->write(&pos, expected_control_mode);
368  received_control_mode = client_->getControlMode();
369 
370  EXPECT_EQ(toUnderlying(expected_control_mode), received_control_mode);
371 }
372 
373 TEST_F(ReverseIntefaceTest, write_freedrive_control_message)
374 {
375  // Wait for the client to connect to the server
376  EXPECT_TRUE(waitForProgramState(1000, true));
377 
379  reverse_interface_->writeFreedriveControlMessage(written_freedrive_message);
380  int32_t received_freedrive_message = client_->getFreedriveControlMode();
381 
382  EXPECT_EQ(toUnderlying(written_freedrive_message), received_freedrive_message);
383 
384  written_freedrive_message = control::FreedriveControlMessage::FREEDRIVE_NOOP;
385  reverse_interface_->writeFreedriveControlMessage(written_freedrive_message);
386  received_freedrive_message = client_->getFreedriveControlMode();
387 
388  EXPECT_EQ(toUnderlying(written_freedrive_message), received_freedrive_message);
389 
390  written_freedrive_message = control::FreedriveControlMessage::FREEDRIVE_START;
391  reverse_interface_->writeFreedriveControlMessage(written_freedrive_message);
392  received_freedrive_message = client_->getFreedriveControlMode();
393 
394  EXPECT_EQ(toUnderlying(written_freedrive_message), received_freedrive_message);
395 }
396 
397 int main(int argc, char* argv[])
398 {
399  ::testing::InitGoogleTest(&argc, argv);
400 
401  return RUN_ALL_TESTS();
402 }
virtual bool open(int socket_fd, struct sockaddr *address, size_t address_len)
bool waitForProgramState(int milliseconds=100, bool program_state=true)
std::unique_ptr< Client > client_
Set when speedj control is active.
Represents command to start a new trajectory.
Set when trajectory forwarding is active.
Represents command to cancel currently active trajectory.
FreedriveControlMessage
Control messages for starting and stopping freedrive mode.
void handleProgramState(bool program_state)
void readMessage(int32_t &keep_alive_signal, vector6int32_t &pos, int32_t &control_mode)
Set when no controller is currently active controlling the robot.
Set when servoj control is active.
TEST_F(ReverseIntefaceTest, handle_program_state)
int main(int argc, char *argv[])
std::condition_variable program_running_
ControlMode
Control modes as interpreted from the script runnning on the robot.
Definition: control_mode.h:39
When this is set, the program is expected to stop and exit.
Startup default until another mode is sent to the script.
Represents command to start freedrive mode.
std::array< int32_t, 6 > vector6int32_t
Definition: types.h:31
Class for TCP socket abstraction.
Definition: tcp_socket.h:48
constexpr std::underlying_type< E >::type toUnderlying(const E e) noexcept
Converts an enum type to its underlying type.
Definition: types.h:58
TrajectoryControlMessage
Control messages for forwarding and aborting trajectories.
Socket is connected and ready to use.
std::unique_ptr< control::ReverseInterface > reverse_interface_
std::array< double, 6 > vector6d_t
Definition: types.h:30
Set when cartesian velocity control is active.
Set when cartesian pose control is active.
Represents keep running in freedrive mode.
The ReverseInterface class handles communication to the robot. It starts a server and waits for the r...
Represents command to stop freedrive mode.


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