Skip to content

Commit b90a3ec

Browse files
author
Felix Exner (fexner)
authored
Merge pull request #172 from fmauch/max_num_tries
Add support for setting socket max num tries and reconnect timeout
2 parents 7626fe5 + 174e0b5 commit b90a3ec

File tree

15 files changed

+211
-37
lines changed

15 files changed

+211
-37
lines changed

include/ur_client_library/comm/pipeline.h

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -172,8 +172,13 @@ class IProducer
172172
public:
173173
/*!
174174
* \brief Set-up functionality of the producers.
175+
*
176+
* \param max_num_tries Maximum number of connection attempts before counting the connection as
177+
* failed. Unlimited number of attempts when set to 0.
178+
* \param reconnection_time time in between connection attempts to the server
175179
*/
176-
virtual void setupProducer()
180+
virtual void setupProducer(const size_t max_num_tries = 0,
181+
const std::chrono::milliseconds reconnection_time = std::chrono::seconds(10))
177182
{
178183
}
179184
/*!
@@ -287,9 +292,17 @@ class Pipeline
287292
stop();
288293
}
289294

290-
void init()
295+
/*!
296+
* \brief Initialize the pipeline. Internally calls setup of producer and consumer.
297+
*
298+
* \param max_num_tries Maximum number of connection attempts before counting the connection as
299+
* failed. Unlimited number of attempts when set to 0.
300+
* \param reconnection_time time in between connection attempts to the server
301+
*/
302+
void init(const size_t max_num_tries = 0,
303+
const std::chrono::milliseconds reconnection_time = std::chrono::seconds(10))
291304
{
292-
producer_.setupProducer();
305+
producer_.setupProducer(max_num_tries, reconnection_time);
293306
if (consumer_ != nullptr)
294307
consumer_->setupConsumer();
295308
}

include/ur_client_library/comm/producer.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,19 @@ class URProducer : public IProducer<T>
5959

6060
/*!
6161
* \brief Triggers the stream to connect to the robot.
62+
*
63+
* \param max_num_tries Maximum number of connection attempts before counting the connection as
64+
* failed. Unlimited number of attempts when set to 0.
65+
* \param reconnection_time time in between connection attempts to the server
6266
*/
63-
void setupProducer() override
67+
void setupProducer(const size_t max_num_tries = 0,
68+
const std::chrono::milliseconds reconnection_time = std::chrono::seconds(10)) override
6469
{
6570
timeval tv;
6671
tv.tv_sec = 1;
6772
tv.tv_usec = 0;
6873
stream_.setReceiveTimeout(tv);
69-
if (!stream_.connect())
74+
if (!stream_.connect(max_num_tries, reconnection_time))
7075
{
7176
throw UrException("Failed to connect to robot. Please check if the robot is booted and connected.");
7277
}

include/ur_client_library/comm/stream.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <sys/socket.h>
2424
#include <sys/types.h>
2525
#include <atomic>
26+
#include <chrono>
2627
#include <mutex>
2728
#include <string>
2829
#include "ur_client_library/log.h"
@@ -56,11 +57,16 @@ class URStream : public TCPSocket
5657
/*!
5758
* \brief Connects to the configured socket.
5859
*
60+
* \param max_num_tries Maximum number of connection attempts before counting the connection as
61+
* failed. Unlimited number of attempts when set to 0.
62+
* \param reconnection_time time in between connection attempts to the server
63+
*
5964
* \returns True on success, false if connection could not be established
6065
*/
61-
bool connect()
66+
bool connect(const size_t max_num_tries = 0,
67+
const std::chrono::milliseconds reconnection_time = std::chrono::seconds(10))
6268
{
63-
return TCPSocket::setup(host_, port_);
69+
return TCPSocket::setup(host_, port_, max_num_tries, reconnection_time);
6470
}
6571

6672
/*!

include/ur_client_library/comm/tcp_socket.h

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <sys/socket.h>
2424
#include <sys/types.h>
2525
#include <atomic>
26+
#include <chrono>
2627
#include <mutex>
2728
#include <string>
2829
#include <memory>
@@ -50,7 +51,8 @@ class TCPSocket
5051
private:
5152
std::atomic<int> socket_fd_;
5253
std::atomic<SocketState> state_;
53-
std::chrono::seconds reconnection_time_;
54+
std::chrono::milliseconds reconnection_time_;
55+
bool reconnection_time_modified_deprecated_ = false;
5456

5557
void setupOptions();
5658

@@ -60,11 +62,13 @@ class TCPSocket
6062
return ::connect(socket_fd, address, address_len) == 0;
6163
}
6264

63-
bool setup(std::string& host, int port);
65+
bool setup(const std::string& host, const int port, const size_t max_num_tries = 0,
66+
const std::chrono::milliseconds reconnection_time = DEFAULT_RECONNECTION_TIME);
6467

6568
std::unique_ptr<timeval> recv_timeout_;
6669

6770
public:
71+
static constexpr std::chrono::milliseconds DEFAULT_RECONNECTION_TIME{ 10000 };
6872
/*!
6973
* \brief Creates a TCPSocket object
7074
*/
@@ -147,10 +151,8 @@ class TCPSocket
147151
*
148152
* \param reconnection_time time in between connection attempts to the server
149153
*/
150-
void setReconnectionTime(std::chrono::seconds reconnection_time)
151-
{
152-
reconnection_time_ = reconnection_time;
153-
}
154+
[[deprecated("Reconnection time is passed to setup directly now.")]] void
155+
setReconnectionTime(const std::chrono::milliseconds reconnection_time);
154156
};
155157
} // namespace comm
156158
} // namespace urcl

include/ur_client_library/rtde/rtde_client.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,9 +123,14 @@ class RTDEClient
123123
* \brief Sets up RTDE communication with the robot. The handshake includes negotiation of the
124124
* used protocol version and setting of input and output recipes.
125125
*
126+
* \param max_num_tries Maximum number of connection attempts before counting the connection as
127+
* failed. Unlimited number of attempts when set to 0.
128+
* \param reconnection_time time in between connection attempts to the server
129+
*
126130
* \returns Success of the handshake
127131
*/
128-
bool init();
132+
bool init(const size_t max_num_tries = 0,
133+
const std::chrono::milliseconds reconnection_time = std::chrono::seconds(10));
129134
/*!
130135
* \brief Triggers the robot to start sending RTDE data packages in the negotiated format.
131136
*
@@ -228,7 +233,8 @@ class RTDEClient
228233
// the robot is booted.
229234
std::vector<std::string> ensureTimestampIsPresent(const std::vector<std::string>& output_recipe) const;
230235

231-
void setupCommunication();
236+
void setupCommunication(const size_t max_num_tries = 0,
237+
const std::chrono::milliseconds reconnection_time = std::chrono::seconds(10));
232238
bool negotiateProtocolVersion(const uint16_t protocol_version);
233239
void queryURControlVersion();
234240
void setupOutputs(const uint16_t protocol_version);

include/ur_client_library/ur/dashboard_client.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,14 +55,19 @@ class DashboardClient : public comm::TCPSocket
5555
DashboardClient() = delete;
5656
virtual ~DashboardClient() = default;
5757

58-
const int DASHBOARD_SERVER_PORT = 29999;
58+
static constexpr int DASHBOARD_SERVER_PORT = 29999;
5959

6060
/*!
6161
* \brief Opens a connection to the dashboard server on the host as specified in the constructor.
6262
*
63+
* \param max_num_tries Maximum number of connection attempts before counting the connection as
64+
* failed. Unlimited number of attempts when set to 0.
65+
* \param reconnection_time time in between connection attempts to the server
66+
*
6367
* \returns True on successful connection, false otherwise.
6468
*/
65-
bool connect();
69+
bool connect(const size_t max_num_tries = 0,
70+
const std::chrono::milliseconds reconnection_time = std::chrono::seconds(10));
6671

6772
/*!
6873
* \brief Makes sure no connection to the dashboard server is held inside the object.

src/comm/tcp_socket.cpp

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <endian.h>
2525
#include <netinet/tcp.h>
2626
#include <unistd.h>
27+
#include <chrono>
2728
#include <cstring>
2829
#include <sstream>
2930
#include <thread>
@@ -55,8 +56,19 @@ void TCPSocket::setupOptions()
5556
}
5657
}
5758

58-
bool TCPSocket::setup(std::string& host, int port)
59+
bool TCPSocket::setup(const std::string& host, const int port, const size_t max_num_tries,
60+
const std::chrono::milliseconds reconnection_time)
5961
{
62+
// This can be removed once we remove the setReconnectionTime() method
63+
auto reconnection_time_resolved = reconnection_time;
64+
if (reconnection_time_modified_deprecated_)
65+
{
66+
URCL_LOG_WARN("TCPSocket::setup(): Reconnection time was modified using `setReconnectionTime()` which is "
67+
"deprecated. Please change your code to set reconnection_time through the `setup()` method "
68+
"directly. The value passed to this function will be ignored.");
69+
reconnection_time_resolved = reconnection_time_;
70+
}
71+
6072
if (state_ == SocketState::Connected)
6173
return false;
6274

@@ -74,6 +86,7 @@ bool TCPSocket::setup(std::string& host, int port)
7486
hints.ai_socktype = SOCK_STREAM;
7587
hints.ai_flags = AI_PASSIVE;
7688

89+
size_t connect_counter = 0;
7790
bool connected = false;
7891
while (!connected)
7992
{
@@ -96,15 +109,25 @@ bool TCPSocket::setup(std::string& host, int port)
96109

97110
freeaddrinfo(result);
98111

112+
if (max_num_tries > 0)
113+
{
114+
if (connect_counter++ >= max_num_tries)
115+
{
116+
URCL_LOG_ERROR("Failed to establish connection for %s:%d after %d tries", host.c_str(), port, max_num_tries);
117+
state_ = SocketState::Invalid;
118+
return false;
119+
}
120+
}
121+
99122
if (!connected)
100123
{
101124
state_ = SocketState::Invalid;
102125
std::stringstream ss;
103126
ss << "Failed to connect to robot on IP " << host_name
104127
<< ". Please check that the robot is booted and reachable on " << host_name << ". Retrying in "
105-
<< reconnection_time_.count() << " seconds";
128+
<< std::chrono::duration_cast<std::chrono::duration<float>>(reconnection_time_resolved).count() << " seconds";
106129
URCL_LOG_ERROR("%s", ss.str().c_str());
107-
std::this_thread::sleep_for(reconnection_time_);
130+
std::this_thread::sleep_for(reconnection_time_resolved);
108131
}
109132
}
110133
setupOptions();
@@ -210,5 +233,13 @@ void TCPSocket::setReceiveTimeout(const timeval& timeout)
210233
}
211234
}
212235

236+
void TCPSocket::setReconnectionTime(const std::chrono::milliseconds reconnection_time)
237+
{
238+
URCL_LOG_ERROR("Calling setReconnectionTime is deprecated. Reconnection timeout is passed to the setup method "
239+
"directly.");
240+
reconnection_time_ = reconnection_time;
241+
reconnection_time_modified_deprecated_ = true;
242+
}
243+
213244
} // namespace comm
214245
} // namespace urcl

src/rtde/rtde_client.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ RTDEClient::~RTDEClient()
6969
disconnect();
7070
}
7171

72-
bool RTDEClient::init()
72+
bool RTDEClient::init(const size_t max_num_tries, const std::chrono::milliseconds reconnection_time)
7373
{
7474
if (client_state_ > ClientState::UNINITIALIZED)
7575
{
@@ -79,7 +79,7 @@ bool RTDEClient::init()
7979
unsigned int attempts = 0;
8080
while (attempts < MAX_INITIALIZE_ATTEMPTS)
8181
{
82-
setupCommunication();
82+
setupCommunication(max_num_tries, reconnection_time);
8383
if (client_state_ == ClientState::INITIALIZED)
8484
return true;
8585

@@ -92,11 +92,11 @@ bool RTDEClient::init()
9292
throw UrException(ss.str());
9393
}
9494

95-
void RTDEClient::setupCommunication()
95+
void RTDEClient::setupCommunication(const size_t max_num_tries, const std::chrono::milliseconds reconnection_time)
9696
{
9797
client_state_ = ClientState::INITIALIZING;
9898
// A running pipeline is needed inside setup
99-
pipeline_.init();
99+
pipeline_.init(max_num_tries, reconnection_time);
100100
pipeline_.run();
101101

102102
uint16_t protocol_version = MAX_RTDE_PROTOCOL_VERSION;

src/ur/dashboard_client.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ void DashboardClient::rtrim(std::string& str, const std::string& chars)
4747
str.erase(str.find_last_not_of(chars) + 1);
4848
}
4949

50-
bool DashboardClient::connect()
50+
bool DashboardClient::connect(const size_t max_num_tries, const std::chrono::milliseconds reconnection_time)
5151
{
5252
if (getState() == comm::SocketState::Connected)
5353
{
@@ -67,11 +67,15 @@ bool DashboardClient::connect()
6767
TCPSocket::setReceiveTimeout(tv);
6868
try
6969
{
70-
if (TCPSocket::setup(host_, port_))
70+
if (TCPSocket::setup(host_, port_, max_num_tries, reconnection_time))
7171
{
7272
URCL_LOG_INFO("%s", read().c_str());
7373
ret_val = true;
7474
}
75+
else
76+
{
77+
return false;
78+
}
7579
}
7680
catch (const TimeoutException&)
7781
{

tests/test_dashboard_client.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include <ur_client_library/exceptions.h>
3333
#include <chrono>
3434
#include <thread>
35+
#include "ur_client_library/comm/tcp_socket.h"
3536
#include "ur_client_library/ur/version_information.h"
3637
#define private public
3738
#include <ur_client_library/ur/dashboard_client.h>
@@ -203,6 +204,21 @@ TEST_F(DashboardClientTest, set_receive_timeout)
203204
}
204205
}
205206

207+
TEST_F(DashboardClientTest, connect_non_running_robot)
208+
{
209+
std::unique_ptr<DashboardClient> dashboard_client;
210+
// We use an IP address on the integration_test's subnet
211+
dashboard_client.reset(new DashboardClient("192.168.56.123"));
212+
auto start = std::chrono::system_clock::now();
213+
EXPECT_FALSE(dashboard_client->connect(2, std::chrono::milliseconds(500)));
214+
auto end = std::chrono::system_clock::now();
215+
auto elapsed = end - start;
216+
// This is only a rough estimate, obviously.
217+
// Since this isn't done on the loopback device, trying to open a socket on a non-existing address
218+
// takes considerably longer.
219+
EXPECT_LT(elapsed, 2 * comm::TCPSocket::DEFAULT_RECONNECTION_TIME);
220+
}
221+
206222
int main(int argc, char* argv[])
207223
{
208224
::testing::InitGoogleTest(&argc, argv);

0 commit comments

Comments
 (0)