Skip to content

Commit 1d13676

Browse files
committed
Added tests for the primary client and primary parser.
1 parent 837fd41 commit 1d13676

File tree

3 files changed

+622
-98
lines changed

3 files changed

+622
-98
lines changed

tests/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@ else()
4848
message(STATUS "Skipping integration tests.")
4949
endif()
5050

51+
add_executable(primary_client_tests test_primary_client.cpp)
52+
target_compile_options(primary_client_tests PRIVATE ${CXX17_FLAG})
53+
target_include_directories(primary_client_tests PRIVATE ${GTEST_INCLUDE_DIRS})
54+
target_link_libraries(primary_client_tests PRIVATE ur_client_library::urcl ${GTEST_LIBRARIES})
55+
gtest_add_tests(TARGET primary_client_tests
56+
)
5157

5258
add_executable(primary_parser_tests test_primary_parser.cpp)
5359
target_compile_options(primary_parser_tests PRIVATE ${CXX17_FLAG})

tests/test_primary_client.cpp

Lines changed: 296 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,296 @@
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

Comments
 (0)