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
+ #include < chrono>
34
+
35
+ #include < ur_client_library/comm/tcp_server.h>
36
+ #define private public
37
+ #include < ur_client_library/primary/primary_client.h>
38
+
39
+ using namespace urcl ;
40
+
41
+ std::string ROBOT_IP = " 127.0.0.1" ;
42
+ int PRIMARY_PORT = 30001 ;
43
+ int DASHBOARD_PORT = 29999 ;
44
+ int FAKE_PRIMARY_PORT = 60061 ;
45
+ int FAKE_DASHBOARD_PORT = 60059 ;
46
+
47
+ class PrimaryClientTest : public ::testing::Test
48
+ {
49
+ protected:
50
+ void SetUp ()
51
+ {
52
+ in_remote_control_ = true ;
53
+ }
54
+
55
+ void TearDown ()
56
+ {
57
+ dashboard_server_.reset ();
58
+ primary_server_.reset ();
59
+ client_.reset ();
60
+ }
61
+
62
+ void run ()
63
+ {
64
+ unsigned char message[] = { 0x00 , 0x00 , 0x00 , 0x50 , 0x19 , 0x00 , 0x00 , 0x00 ,
65
+ 0x00 , 0x56 , 0x76 , 0xd3 , 0xa0 , 0x00 , 0x00 , 0x00 }; // Empty GlobalVariablesSetupMessage
66
+ size_t len = sizeof (message);
67
+ size_t written;
68
+ while (running_)
69
+ {
70
+ primary_server_->write (client_fd_, message, len, written);
71
+ std::this_thread::sleep_for (std::chrono::milliseconds (100 ));
72
+ }
73
+ }
74
+
75
+ void stopThread ()
76
+ {
77
+ URCL_LOG_DEBUG (" Shutting down thread" );
78
+ running_ = false ;
79
+ if (server_thread_.joinable ())
80
+ {
81
+ server_thread_.join ();
82
+ }
83
+ }
84
+
85
+ void connectionCallback (const int filedescriptor)
86
+ {
87
+ std::lock_guard<std::mutex> lk (connect_mutex_);
88
+ client_fd_ = filedescriptor;
89
+ connect_cv_.notify_one ();
90
+ connection_callback_ = true ;
91
+ }
92
+
93
+ void connectionCallbackDB (const int filedescriptor)
94
+ {
95
+ std::lock_guard<std::mutex> lk (connect_mutex_db_);
96
+ client_fd_db_ = filedescriptor;
97
+
98
+ unsigned char message[] = {
99
+ 0x43 , 0x6f , 0x6e , 0x6e , 0x65 , 0x63 , 0x74 , 0x65 , 0x64 , 0x3a , 0x20 , 0x55 , 0x6e , 0x69 , 0x76 , 0x65 , 0x72 , 0x73 , 0x61 ,
100
+ 0x6c , 0x20 , 0x52 , 0x6f , 0x62 , 0x6f , 0x74 , 0x73 , 0x20 , 0x46 , 0x61 , 0x6b , 0x65 , 0x20 , 0x54 , 0x65 , 0x73 , 0x74 , 0x20 ,
101
+ 0x44 , 0x61 , 0x73 , 0x68 , 0x62 , 0x6f , 0x61 , 0x72 , 0x64 , 0x20 , 0x53 , 0x65 , 0x72 , 0x76 , 0x65 , 0x72 , 0x0a
102
+ }; // "Connected: Universal Robots Fake Test Dashboard Server\n"
103
+ size_t len = sizeof (message);
104
+ size_t written;
105
+ dashboard_server_->write (client_fd_db_, message, len, written);
106
+ connect_cv_db_.notify_one ();
107
+ connection_callback_db_ = true ;
108
+ }
109
+
110
+ void messageCallback (const int filedescriptor, char * buffer)
111
+ {
112
+ std::lock_guard<std::mutex> lk (message_mutex_);
113
+ message_ = std::string (buffer);
114
+ message_cv_.notify_one ();
115
+ message_callback_ = true ;
116
+ }
117
+
118
+ void messageCallbackDB (const int filedescriptor, char * buffer)
119
+ {
120
+ std::lock_guard<std::mutex> lk (message_mutex_db_);
121
+ message_db_ = std::string (buffer);
122
+ if (message_db_ == " is in remote control\n " )
123
+ {
124
+ unsigned char message_true[] = { 0x74 , 0x72 , 0x75 , 0x65 , 0x0a }; // "true\n"
125
+ unsigned char message_false[] = { 0x66 , 0x61 , 0x6c , 0x73 , 0x65 , 0x0a }; // "false\n"
126
+
127
+ size_t len = in_remote_control_ ? sizeof (message_true) : sizeof (message_false);
128
+ size_t written;
129
+ dashboard_server_->write (client_fd_db_, in_remote_control_ ? message_true : message_false, len, written);
130
+ }
131
+ if (message_db_ == " PolyscopeVersion\n " )
132
+ {
133
+ unsigned char message_pv[] = {
134
+ 0x55 , 0x52 , 0x53 , 0x6f , 0x66 , 0x74 , 0x77 , 0x61 , 0x72 , 0x65 , 0x20 , 0x35 , 0x2e , 0x31 ,
135
+ 0x32 , 0x2e , 0x32 , 0x2e , 0x31 , 0x31 , 0x30 , 0x31 , 0x35 , 0x33 , 0x34 , 0x20 , 0x28 , 0x4a ,
136
+ 0x75 , 0x6c , 0x20 , 0x30 , 0x36 , 0x20 , 0x32 , 0x30 , 0x32 , 0x32 , 0x29 , 0x0a
137
+ }; // URSoftware 5.12.2.1101534 (Jul 06 2022)
138
+ size_t len = sizeof (message_pv);
139
+ size_t written;
140
+ dashboard_server_->write (client_fd_db_, message_pv, len, written);
141
+ }
142
+
143
+ message_cv_db_.notify_one ();
144
+ message_callback_db_ = true ;
145
+ }
146
+
147
+ bool waitForConnectionCallback (int milliseconds = 100 )
148
+ {
149
+ std::unique_lock<std::mutex> lk (connect_mutex_);
150
+ if (connect_cv_.wait_for (lk, std::chrono::milliseconds (milliseconds)) == std::cv_status::no_timeout ||
151
+ connection_callback_ == true )
152
+ {
153
+ connection_callback_ = false ;
154
+ return true ;
155
+ }
156
+ else
157
+ {
158
+ return false ;
159
+ }
160
+ }
161
+
162
+ bool waitForMessageCallback (int milliseconds = 100 )
163
+ {
164
+ std::unique_lock<std::mutex> lk (message_mutex_);
165
+ if (message_cv_.wait_for (lk, std::chrono::milliseconds (milliseconds)) == std::cv_status::no_timeout ||
166
+ message_callback_ == true )
167
+ {
168
+ message_callback_ = false ;
169
+ return true ;
170
+ }
171
+ else
172
+ {
173
+ return false ;
174
+ }
175
+ }
176
+
177
+ bool waitForMessageCallbackDB (int milliseconds = 100 )
178
+ {
179
+ std::unique_lock<std::mutex> lk (message_mutex_db_);
180
+ if (message_cv_db_.wait_for (lk, std::chrono::milliseconds (milliseconds)) == std::cv_status::no_timeout ||
181
+ message_callback_db_ == true )
182
+ {
183
+ message_callback_db_ = false ;
184
+ return true ;
185
+ }
186
+ else
187
+ {
188
+ return false ;
189
+ }
190
+ }
191
+
192
+ std::string message_ = " " ;
193
+ std::string message_db_ = " " ;
194
+ int client_fd_ = -1 ;
195
+ int client_fd_db_ = -1 ;
196
+
197
+ std::atomic<bool > running_;
198
+ std::thread server_thread_;
199
+ std::unique_ptr<primary_interface::PrimaryClient> client_;
200
+ std::unique_ptr<comm::TCPServer> primary_server_;
201
+ std::unique_ptr<comm::TCPServer> dashboard_server_;
202
+
203
+ std::condition_variable connect_cv_, connect_cv_db_;
204
+ std::mutex connect_mutex_, connect_mutex_db_;
205
+
206
+ std::condition_variable message_cv_, message_cv_db_;
207
+ std::mutex message_mutex_, message_mutex_db_;
208
+
209
+ bool connection_callback_ = false ;
210
+ bool connection_callback_db_ = false ;
211
+ bool message_callback_ = false ;
212
+ bool message_callback_db_ = false ;
213
+
214
+ bool in_remote_control_;
215
+ };
216
+
217
+ TEST_F (PrimaryClientTest, check_remote_control)
218
+ {
219
+ dashboard_server_.reset (new comm::TCPServer (FAKE_DASHBOARD_PORT));
220
+ primary_server_.reset (new comm::TCPServer (FAKE_PRIMARY_PORT));
221
+ primary_server_->setMessageCallback (std::bind (&PrimaryClientTest_check_remote_control_Test::messageCallback, this ,
222
+ std::placeholders::_1, std::placeholders::_2));
223
+ dashboard_server_->setMessageCallback (std::bind (&PrimaryClientTest_check_remote_control_Test::messageCallbackDB, this ,
224
+ std::placeholders::_1, std::placeholders::_2));
225
+ primary_server_->setConnectCallback (
226
+ std::bind (&PrimaryClientTest_check_remote_control_Test::connectionCallback, this , std::placeholders::_1));
227
+ dashboard_server_->setConnectCallback (
228
+ std::bind (&PrimaryClientTest_check_remote_control_Test::connectionCallbackDB, this , std::placeholders::_1));
229
+ dashboard_server_->start ();
230
+ primary_server_->start ();
231
+ server_thread_ = std::thread (&PrimaryClientTest_check_remote_control_Test::run, this );
232
+
233
+ std::unique_ptr<primary_interface::PrimaryClient> temp_client;
234
+ client_.reset (new primary_interface::PrimaryClient (ROBOT_IP, " " ));
235
+ std::this_thread::sleep_for (std::chrono::milliseconds (1000 )); // Let connections set up
236
+
237
+ // Disconnect from URSim servers and connect to fake servers
238
+ client_->pipeline_ ->stop ();
239
+ client_->stream_ ->disconnect ();
240
+ client_->dashboard_client_ ->disconnect ();
241
+ client_->dashboard_client_ ->port_ = FAKE_DASHBOARD_PORT;
242
+ client_->stream_ ->port_ = FAKE_PRIMARY_PORT;
243
+ client_->stream_ ->connect ();
244
+ client_->dashboard_client_ ->connect ();
245
+ client_->pipeline_ ->run ();
246
+ std::this_thread::sleep_for (std::chrono::milliseconds (1000 )); // let connections set up
247
+
248
+ // When in_remote_control_ is true the primary client should be able to send script
249
+ in_remote_control_ = true ;
250
+ client_->sendScript (" true\n " );
251
+ EXPECT_TRUE (waitForMessageCallback (1000 ));
252
+
253
+ // Make sure thread sets in_remote_control_ to false and primary client has time to reconnect
254
+ in_remote_control_ = false ;
255
+ std::this_thread::sleep_for (std::chrono::milliseconds (1000 ));
256
+
257
+ // When in_remote_control_ is false the primary client NOT should be able to send script
258
+ client_->sendScript (" false\n " );
259
+ EXPECT_FALSE (waitForMessageCallback (1000 ));
260
+
261
+ stopThread ();
262
+ }
263
+
264
+ TEST_F (PrimaryClientTest, send_script)
265
+ {
266
+ client_.reset (new primary_interface::PrimaryClient (ROBOT_IP, " " ));
267
+ std::stringstream cmd;
268
+ cmd.imbue (std::locale::classic ()); // Make sure, decimal divider is actually '.'
269
+ cmd << " sec setup():" << std::endl
270
+ << " textmsg(\" Command through primary interface complete \" )" << std::endl
271
+ << " end" ;
272
+
273
+ std::string script_code = cmd.str ();
274
+ auto program_with_newline = script_code + ' \n ' ;
275
+ // Should always return false in pipeline as robot will never be in remote control
276
+ EXPECT_FALSE (client_->sendScript (program_with_newline));
277
+ }
278
+
279
+ TEST_F (PrimaryClientTest, get_data)
280
+ {
281
+ client_.reset (new primary_interface::PrimaryClient (ROBOT_IP, " " ));
282
+ EXPECT_EQ (client_->getVersionMessage ()->build_number_ , 0 );
283
+ vector6d_t zero_array = { 0 };
284
+ EXPECT_EQ (client_->getCartesianInfo ()->tcp_offset_coordinates_ , zero_array);
285
+ EXPECT_EQ (client_->getForceModeData ()->wrench_ , zero_array);
286
+ // getGlobalVariablesSetupMessage() will throw an exception since a program on the robot has not been started while
287
+ // this client has been connected
288
+ EXPECT_THROW (client_->getGlobalVariablesSetupMessage ()->variable_names_ , UrException);
289
+ }
290
+
291
+ int main (int argc, char * argv[])
292
+ {
293
+ ::testing::InitGoogleTest (&argc, argv);
294
+
295
+ return RUN_ALL_TESTS ();
296
+ }
0 commit comments