Skip to content

Commit f04e347

Browse files
Added support for std::vector strings in UrDriver (#408)
Since it is a functionality allowed by the RTDEClient, seems reasonable to also add it to the UrDriver.
1 parent 5093e7c commit f04e347

File tree

7 files changed

+219
-18
lines changed

7 files changed

+219
-18
lines changed

include/ur_client_library/example_robot_wrapper.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,26 @@ class ExampleRobotWrapper
7676
ExampleRobotWrapper(const std::string& robot_ip, const std::string& output_recipe_file,
7777
const std::string& input_recipe_file, const bool headless_mode = true,
7878
const std::string& autostart_program = "", const std::string& script_file = SCRIPT_FILE);
79+
/*!
80+
* \brief Construct a new Example Robot Wrapper object
81+
*
82+
* This will connect to a robot and initialize it. In headless mode the program will be running
83+
* instantly, in teach pendant mode the from \p autostart_program will be started.
84+
*
85+
* Note: RTDE communication has to be started separately.
86+
*
87+
* \param robot_ip IP address of the robot to connect to
88+
* \param output_recipe Output recipe vector for RTDE communication
89+
* \param input_recipe Input recipe vector for RTDE communication
90+
* \param headless_mode Should the driver be started in headless mode or not?
91+
* \param autostart_program Program to start automatically after initialization when not in
92+
* headless mode. This flag is ignored in headless mode.
93+
* \param script_file URScript file to send to the robot. That should be script code
94+
* communicating to the driver's reverse interface and trajectory interface.
95+
*/
96+
ExampleRobotWrapper(const std::string& robot_ip, const std::vector<std::string> output_recipe,
97+
const std::vector<std::string> input_recipe, const bool headless_mode = true,
98+
const std::string& autostart_program = "", const std::string& script_file = SCRIPT_FILE);
7999
~ExampleRobotWrapper();
80100

81101
/**

include/ur_client_library/rtde/rtde_client.h

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,19 @@ class RTDEClient
222222
return output_recipe_;
223223
}
224224

225+
/*!
226+
* \brief Getter for the RTDE input recipe.
227+
*
228+
* \returns The input recipe
229+
*/
230+
std::vector<std::string> getInputRecipe()
231+
{
232+
return input_recipe_;
233+
}
234+
235+
// Reads output or input recipe from a file
236+
static std::vector<std::string> readRecipe(const std::string& recipe_file);
237+
225238
private:
226239
comm::URStream<RTDEPackage> stream_;
227240
std::vector<std::string> output_recipe_;
@@ -247,9 +260,6 @@ class RTDEClient
247260
constexpr static const double CB3_MAX_FREQUENCY = 125.0;
248261
constexpr static const double URE_MAX_FREQUENCY = 500.0;
249262

250-
// Reads output or input recipe from a file
251-
std::vector<std::string> readRecipe(const std::string& recipe_file) const;
252-
253263
// Helper function to ensure that timestamp is present in the output recipe. The timestamp is needed to ensure that
254264
// the robot is booted.
255265
std::vector<std::string> ensureTimestampIsPresent(const std::vector<std::string>& output_recipe) const;

include/ur_client_library/ur/ur_driver.h

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,18 +52,20 @@ namespace urcl
5252
*/
5353
struct UrDriverConfiguration
5454
{
55-
std::string robot_ip; //!< IP-address under which the robot is reachable.
56-
std::string script_file; //!< URScript file that should be sent to the robot.
57-
std::string output_recipe_file; //!< Filename where the output recipe is stored in.
58-
std::string input_recipe_file; //!< Filename where the input recipe is stored in.
55+
std::string robot_ip; //!< IP-address under which the robot is reachable.
56+
std::string script_file; //!< URScript file that should be sent to the robot.
57+
std::string output_recipe_file; //!< Filename where the output recipe is stored in.
58+
std::string input_recipe_file; //!< Filename where the input recipe is stored in.
59+
std::vector<std::string> output_recipe; //!< Vector with the output recipe fields.
60+
std::vector<std::string> input_recipe; //!< Vector with the input recipe fields.
5961

6062
/*!
6163
* \brief Function handle to a callback on program state changes.
6264
*
6365
* For this to work, the URScript program will have to send keepalive signals to the \p
6466
* reverse_port.
6567
*/
66-
std::function<void(bool)> handle_program_state;
68+
std::function<void(bool)> handle_program_state = nullptr;
6769
bool headless_mode; //!< Parameter to control if the driver should be started in headless mode.
6870

6971
std::unique_ptr<ToolCommSetup> tool_comm_setup = nullptr; //!< Configuration for using the tool communication.
@@ -837,6 +839,13 @@ class UrDriver
837839
*/
838840
std::vector<std::string> getRTDEOutputRecipe();
839841

842+
/*!
843+
* \brief Getter for the RTDE input recipe used in the RTDE client.
844+
*
845+
* \returns The used RTDE input recipe
846+
*/
847+
std::vector<std::string> getRTDEInputRecipe();
848+
840849
/*!
841850
* \brief Set the Keepalive count. This will set the number of allowed timeout reads on the robot.
842851
*
@@ -987,6 +996,7 @@ class UrDriver
987996
private:
988997
void init(const UrDriverConfiguration& config);
989998

999+
void setupRTDEClient(const UrDriverConfiguration& config);
9901000
void initRTDE();
9911001
void setupReverseInterface(const uint32_t reverse_port);
9921002

@@ -1023,4 +1033,4 @@ class UrDriver
10231033
VersionInformation robot_version_;
10241034
};
10251035
} // namespace urcl
1026-
#endif // ifndef UR_CLIENT_LIBRARY_UR_UR_DRIVER_H_INCLUDED
1036+
#endif // ifndef UR_CLIENT_LIBRARY_UR_UR_DRIVER_H_INCLUDED

src/control/reverse_interface.cpp

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,10 @@ ReverseInterface::ReverseInterface(const ReverseInterfaceConfig& config)
4747
, step_time_(config.step_time)
4848
, keep_alive_count_modified_deprecated_(false)
4949
{
50-
handle_program_state_(false);
50+
if (handle_program_state_)
51+
{
52+
handle_program_state_(false);
53+
}
5154
server_.setMessageCallback(std::bind(&ReverseInterface::messageCallback, this, std::placeholders::_1,
5255
std::placeholders::_2, std::placeholders::_3));
5356
server_.setConnectCallback(std::bind(&ReverseInterface::connectionCallback, this, std::placeholders::_1));
@@ -224,7 +227,10 @@ void ReverseInterface::connectionCallback(const socket_t filedescriptor)
224227
{
225228
URCL_LOG_INFO("Robot connected to reverse interface. Ready to receive control commands.");
226229
client_fd_ = filedescriptor;
227-
handle_program_state_(true);
230+
if (handle_program_state_)
231+
{
232+
handle_program_state_(true);
233+
}
228234
}
229235
else
230236
{
@@ -237,7 +243,10 @@ void ReverseInterface::disconnectionCallback(const socket_t filedescriptor)
237243
{
238244
URCL_LOG_INFO("Connection to reverse interface dropped.", filedescriptor);
239245
client_fd_ = INVALID_SOCKET;
240-
handle_program_state_(false);
246+
if (handle_program_state_)
247+
{
248+
handle_program_state_(false);
249+
}
241250
for (auto handler : disconnect_callbacks_)
242251
{
243252
handler.function(filedescriptor);
@@ -251,4 +260,4 @@ void ReverseInterface::messageCallback(const socket_t filedescriptor, char* buff
251260
}
252261

253262
} // namespace control
254-
} // namespace urcl
263+
} // namespace urcl

src/rtde/rtde_client.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -664,7 +664,7 @@ bool RTDEClient::sendPause()
664664
throw UrException(ss.str());
665665
}
666666

667-
std::vector<std::string> RTDEClient::readRecipe(const std::string& recipe_file) const
667+
std::vector<std::string> RTDEClient::readRecipe(const std::string& recipe_file)
668668
{
669669
std::vector<std::string> recipe;
670670
std::ifstream file(recipe_file);

src/ur/ur_driver.cpp

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,16 @@
3232
//----------------------------------------------------------------------
3333

3434
#include "ur_client_library/ur/ur_driver.h"
35+
#include "ur_client_library/rtde/rtde_client.h"
3536
#include "ur_client_library/control/script_reader.h"
3637
#include "ur_client_library/exceptions.h"
3738
#include "ur_client_library/helpers.h"
3839
#include "ur_client_library/primary/primary_parser.h"
3940
#include "ur_client_library/helpers.h"
4041
#include <memory>
4142
#include <sstream>
43+
#include <stdexcept>
44+
#include <filesystem>
4245

4346
#include <ur_client_library/ur/calibration_checker.h>
4447

@@ -76,14 +79,12 @@ void UrDriver::init(const UrDriverConfiguration& config)
7679

7780
URCL_LOG_DEBUG("Initializing urdriver");
7881
URCL_LOG_DEBUG("Initializing RTDE client");
79-
rtde_client_.reset(
80-
new rtde_interface::RTDEClient(robot_ip_, notifier_, config.output_recipe_file, config.input_recipe_file));
8182

8283
primary_client_.reset(new urcl::primary_interface::PrimaryClient(robot_ip_, notifier_));
8384

8485
get_packet_timeout_ = non_blocking_read_ ? 0 : 100;
8586

86-
initRTDE();
87+
setupRTDEClient(config);
8788
setupReverseInterface(config.reverse_port);
8889

8990
// Figure out the ip automatically if the user didn't provide it
@@ -623,6 +624,11 @@ std::vector<std::string> UrDriver::getRTDEOutputRecipe()
623624
return rtde_client_->getOutputRecipe();
624625
}
625626

627+
std::vector<std::string> UrDriver::getRTDEInputRecipe()
628+
{
629+
return rtde_client_->getInputRecipe();
630+
}
631+
626632
void UrDriver::setKeepaliveCount(const uint32_t count)
627633
{
628634
URCL_LOG_WARN("DEPRECATION NOTICE: Setting the keepalive count has been deprecated. Instead use the "
@@ -684,4 +690,40 @@ std::deque<urcl::primary_interface::ErrorCode> UrDriver::getErrorCodes()
684690
{
685691
return primary_client_->getErrorCodes();
686692
}
693+
694+
void UrDriver::setupRTDEClient(const UrDriverConfiguration& config)
695+
{
696+
auto output_recipe = config.output_recipe;
697+
if (config.output_recipe_file.empty() && config.output_recipe.size() == 0)
698+
{
699+
throw UrException("Neither output recipe file nor output recipe have been defined. An output recipe is required.");
700+
}
701+
if (!config.output_recipe_file.empty())
702+
{
703+
if (config.output_recipe.size() != 0)
704+
{
705+
URCL_LOG_WARN("Both output recipe file and output recipe vector are used. Defaulting to output recipe vector");
706+
}
707+
else
708+
{
709+
output_recipe = rtde_interface::RTDEClient::readRecipe(config.output_recipe_file);
710+
}
711+
}
712+
713+
auto input_recipe = config.input_recipe;
714+
if (!config.input_recipe_file.empty())
715+
{
716+
if (config.input_recipe.size() != 0)
717+
{
718+
URCL_LOG_WARN("Both input recipe file and input recipe vector are used. Defaulting to input recipe vector.");
719+
}
720+
else
721+
{
722+
input_recipe = rtde_interface::RTDEClient::readRecipe(config.input_recipe_file);
723+
}
724+
}
725+
726+
resetRTDEClient(output_recipe, input_recipe);
727+
}
728+
687729
} // namespace urcl

tests/test_ur_driver.cpp

Lines changed: 111 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,51 @@
3333
#include <ur_client_library/ur/dashboard_client.h>
3434
#include <ur_client_library/ur/ur_driver.h>
3535
#include <ur_client_library/example_robot_wrapper.h>
36+
#include <algorithm>
3637
#include "test_utils.h"
3738

3839
using namespace urcl;
3940

4041
const std::string SCRIPT_FILE = "../resources/external_control.urscript";
4142
const std::string OUTPUT_RECIPE = "resources/rtde_output_recipe.txt";
4243
const std::string INPUT_RECIPE = "resources/rtde_input_recipe.txt";
44+
const std::vector<std::string> OUTPUT_RECIPE_VECTOR = { "timestamp",
45+
"actual_qd",
46+
"speed_scaling",
47+
"target_speed_fraction",
48+
"runtime_state",
49+
"actual_TCP_force",
50+
"actual_TCP_pose",
51+
"actual_digital_input_bits",
52+
"actual_digital_output_bits",
53+
"standard_analog_input0",
54+
"standard_analog_input1",
55+
"standard_analog_output0",
56+
"standard_analog_output1",
57+
"analog_io_types",
58+
"tool_mode",
59+
"tool_analog_input_types",
60+
"tool_analog_input0",
61+
"tool_analog_input1",
62+
"tool_output_voltage",
63+
"tool_output_current",
64+
"tool_temperature",
65+
"robot_mode",
66+
"safety_mode",
67+
"robot_status_bits",
68+
"safety_status_bits",
69+
"actual_current",
70+
"tcp_offset" };
71+
const std::vector<std::string> INPUT_RECIPE_VECTOR = {
72+
"speed_slider_mask", "standard_digital_output_mask",
73+
"standard_digital_output", "configurable_digital_output_mask",
74+
"configurable_digital_output", "tool_digital_output_mask",
75+
"tool_digital_output", "standard_analog_output_mask",
76+
"standard_analog_output_type", "standard_analog_output_0",
77+
"standard_analog_output_1"
78+
};
79+
const std::string OUTPUT_RECIPE_VECTOR_EXCLUDED_VALUE = "actual_q";
80+
const std::string INPUT_RECIPE_VECTOR_EXCLUDED_VALUE = "speed_slider_fraction";
4381
const std::string CALIBRATION_CHECKSUM = "calib_12788084448423163542";
4482
std::string g_ROBOT_IP = "192.168.56.101";
4583
bool g_HEADLESS = true;
@@ -310,6 +348,78 @@ TEST(UrDriverInitTest, setting_connection_limits_works_correctly)
310348
EXPECT_THROW(UrDriver ur_driver(config), UrException);
311349
}
312350

351+
TEST(UrDriverInitTest, no_recipe_throws_error)
352+
{
353+
UrDriverConfiguration config;
354+
config.socket_reconnect_attempts = 1;
355+
config.socket_reconnection_timeout = std::chrono::milliseconds(200);
356+
config.robot_ip = g_ROBOT_IP; // That IP address should not exist on the test network
357+
config.headless_mode = g_HEADLESS;
358+
359+
EXPECT_THROW(UrDriver ur_driver(config), UrException);
360+
}
361+
362+
TEST(UrDriverInitTest, initialization_from_vectors)
363+
{
364+
UrDriverConfiguration config;
365+
config.socket_reconnect_attempts = 1;
366+
config.socket_reconnection_timeout = std::chrono::milliseconds(200);
367+
config.robot_ip = g_ROBOT_IP; // That IP address should not exist on the test network
368+
config.input_recipe = INPUT_RECIPE_VECTOR;
369+
config.output_recipe = OUTPUT_RECIPE_VECTOR;
370+
config.headless_mode = g_HEADLESS;
371+
config.script_file = SCRIPT_FILE;
372+
373+
EXPECT_NO_THROW(UrDriver ur_driver(config));
374+
}
375+
376+
TEST(UrDriverInitTest, non_existing_output_recipe_file_throws_exception)
377+
{
378+
UrDriverConfiguration config;
379+
config.socket_reconnect_attempts = 1;
380+
config.socket_reconnection_timeout = std::chrono::milliseconds(200);
381+
config.robot_ip = g_ROBOT_IP; // That IP address should not exist on the test network
382+
config.input_recipe_file = INPUT_RECIPE;
383+
config.output_recipe_file = " ";
384+
config.headless_mode = g_HEADLESS;
385+
386+
EXPECT_THROW(UrDriver ur_driver(config), UrException);
387+
}
388+
389+
TEST(UrDriverInitTest, non_existing_input_recipe_file_does_not_throw_exception)
390+
{
391+
UrDriverConfiguration config;
392+
config.socket_reconnect_attempts = 1;
393+
config.socket_reconnection_timeout = std::chrono::milliseconds(200);
394+
config.robot_ip = g_ROBOT_IP; // That IP address should not exist on the test network
395+
config.output_recipe_file = OUTPUT_RECIPE;
396+
config.headless_mode = g_HEADLESS;
397+
config.script_file = SCRIPT_FILE;
398+
399+
EXPECT_NO_THROW(UrDriver ur_driver(config));
400+
}
401+
402+
TEST(UrDriverInitTest, both_recipe_file_and_vector_select_vector)
403+
{
404+
UrDriverConfiguration config;
405+
config.socket_reconnect_attempts = 1;
406+
config.socket_reconnection_timeout = std::chrono::milliseconds(200);
407+
config.robot_ip = g_ROBOT_IP; // That IP address should not exist on the test network
408+
config.input_recipe_file = INPUT_RECIPE;
409+
config.output_recipe_file = OUTPUT_RECIPE;
410+
config.input_recipe = INPUT_RECIPE_VECTOR;
411+
config.output_recipe = OUTPUT_RECIPE_VECTOR;
412+
config.headless_mode = g_HEADLESS;
413+
config.script_file = SCRIPT_FILE;
414+
415+
auto driver = UrDriver(config);
416+
auto output_recipe = driver.getRTDEOutputRecipe();
417+
EXPECT_TRUE(std::find(output_recipe.begin(), output_recipe.end(), OUTPUT_RECIPE_VECTOR_EXCLUDED_VALUE) ==
418+
output_recipe.end());
419+
auto input_recipe = driver.getRTDEInputRecipe();
420+
EXPECT_TRUE(std::find(input_recipe.begin(), input_recipe.end(), INPUT_RECIPE_VECTOR_EXCLUDED_VALUE) ==
421+
input_recipe.end());
422+
}
313423
// TODO we should add more tests for the UrDriver class.
314424

315425
int main(int argc, char* argv[])
@@ -332,4 +442,4 @@ int main(int argc, char* argv[])
332442
}
333443

334444
return RUN_ALL_TESTS();
335-
}
445+
}

0 commit comments

Comments
 (0)