Skip to content

Commit fbe69fc

Browse files
authored
Add helper function to get robot series (UniversalRobots#488)
As discussed in UniversalRobots/Universal_Robots_ROS2_Driver#842 it might be useful to compare a robot model with the connected robot for verification purposes. Since at least for the UR3, UR5 and UR10 there are two models respectively (CB3 and e-series), so we would need to be able to distinguish between them. To do that, this PR adds a `getRobotSeries()` function to the primary client and a `robotSeriesFromTypeAndVersion()` function to deduce the series from the robot model and software version (as the series isn't directly available to my knowledge). I decided to return `UNDEFINED` for combinations that shouldn't show up in the real world (e.g. a CB3 software version on a UR16 or any UR-Series robot).
1 parent 4dda6d5 commit fbe69fc

7 files changed

Lines changed: 169 additions & 0 deletions

File tree

include/ur_client_library/helpers.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@
3232
#include <string>
3333
#include <chrono>
3434
#include <functional>
35+
36+
#include "ur_client_library/ur/version_information.h"
37+
#include "ur_client_library/ur/datatypes.h"
3538
#ifdef _WIN32
3639

3740
# define NOMINMAX
@@ -128,5 +131,15 @@ void clampToUnitRange(std::array<T, N>& values)
128131
}
129132
}
130133

134+
/*!
135+
* \brief Get the robot series from the robot type and version information.
136+
*
137+
* \param type The robot type.
138+
* \param version The version information of the robot.
139+
*
140+
* \returns The robot series corresponding to the given robot type and version information.
141+
*/
142+
RobotSeries robotSeriesFromTypeAndVersion(const RobotType type, const VersionInformation& version);
143+
131144
} // namespace urcl
132145
#endif // ifndef UR_CLIENT_LIBRARY_HELPERS_H_INCLUDED

include/ur_client_library/primary/primary_client.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,13 @@ class PrimaryClient
265265
*/
266266
RobotType getRobotType();
267267

268+
/*!
269+
* \brief Get the Robot series
270+
*
271+
* If no robot type data has been received yet, this will return UNDEFINED.
272+
*/
273+
RobotSeries getRobotSeries();
274+
268275
private:
269276
/*!
270277
* \brief Reconnects the primary stream used to send program to the robot.

include/ur_client_library/ur/datatypes.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,14 @@ enum class RobotType : int8_t
112112
UR15 = 9
113113
};
114114

115+
enum class RobotSeries
116+
{
117+
UNDEFINED = -128,
118+
CB3 = 1,
119+
E_SERIES = 2,
120+
UR_SERIES = 3
121+
};
122+
115123
inline std::string robotModeString(const RobotMode& mode)
116124
{
117125
switch (mode)

src/helpers.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,4 +160,47 @@ std::vector<std::string> splitString(const std::string& input, const std::string
160160
return result;
161161
}
162162

163+
RobotSeries robotSeriesFromTypeAndVersion(const RobotType type, const VersionInformation& version_info)
164+
{
165+
switch (type)
166+
{
167+
case RobotType::UR3:
168+
case RobotType::UR5:
169+
case RobotType::UR10:
170+
if (version_info.major >= 5)
171+
{
172+
return RobotSeries::E_SERIES;
173+
}
174+
else
175+
{
176+
return RobotSeries::CB3;
177+
}
178+
case RobotType::UR16:
179+
if (version_info.major >= 5)
180+
{
181+
return RobotSeries::E_SERIES;
182+
}
183+
else
184+
{
185+
return RobotSeries::UNDEFINED;
186+
}
187+
case RobotType::UR15:
188+
case RobotType::UR18:
189+
case RobotType::UR20:
190+
case RobotType::UR30:
191+
case RobotType::UR8LONG:
192+
if (version_info.major >= 5)
193+
{
194+
return RobotSeries::UR_SERIES;
195+
}
196+
else
197+
{
198+
return RobotSeries::UNDEFINED;
199+
}
200+
case RobotType::UNDEFINED:
201+
return RobotSeries::UNDEFINED;
202+
}
203+
return RobotSeries::UNDEFINED;
204+
}
205+
163206
} // namespace urcl

src/primary/primary_client.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,5 +297,22 @@ RobotType PrimaryClient::getRobotType()
297297
return static_cast<RobotType>(configuration_data->robot_type_);
298298
}
299299

300+
RobotSeries PrimaryClient::getRobotSeries()
301+
{
302+
auto robot_type = getRobotType();
303+
if (robot_type == RobotType::UNDEFINED)
304+
{
305+
return RobotSeries::UNDEFINED;
306+
}
307+
308+
auto version_info = getRobotVersion();
309+
if (version_info == nullptr)
310+
{
311+
return RobotSeries::UNDEFINED;
312+
}
313+
314+
return robotSeriesFromTypeAndVersion(robot_type, *version_info);
315+
}
316+
300317
} // namespace primary_interface
301318
} // namespace urcl

tests/test_helpers.cpp

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232

3333
#include <ur_client_library/helpers.h>
3434
#include <ur_client_library/exceptions.h>
35+
#include <ur_client_library/ur/datatypes.h>
36+
#include <ur_client_library/ur/version_information.h>
3537

3638
using namespace urcl;
3739

@@ -86,3 +88,52 @@ TEST(TestHelpers, splitString)
8688
std::vector<std::string> expected = { "5", "12", "0", "1101319" };
8789
EXPECT_EQ(expected, splitString(version_string1, "."));
8890
}
91+
92+
TEST(TestHelpers, robotSeriesFromTypeAndVersion)
93+
{
94+
const VersionInformation cb3_version = VersionInformation::fromString("3.15.0.0");
95+
const VersionInformation polyscope_5_version = VersionInformation::fromString("5.12.0.1101319");
96+
const VersionInformation polyscope_x_version = VersionInformation::fromString("10.0.0.0");
97+
98+
// UR3/UR5/UR10: major >= 5 -> E_SERIES, otherwise CB3
99+
EXPECT_EQ(robotSeriesFromTypeAndVersion(RobotType::UR3, cb3_version), RobotSeries::CB3);
100+
EXPECT_EQ(robotSeriesFromTypeAndVersion(RobotType::UR5, cb3_version), RobotSeries::CB3);
101+
EXPECT_EQ(robotSeriesFromTypeAndVersion(RobotType::UR10, cb3_version), RobotSeries::CB3);
102+
103+
EXPECT_EQ(robotSeriesFromTypeAndVersion(RobotType::UR3, polyscope_5_version), RobotSeries::E_SERIES);
104+
EXPECT_EQ(robotSeriesFromTypeAndVersion(RobotType::UR5, polyscope_5_version), RobotSeries::E_SERIES);
105+
EXPECT_EQ(robotSeriesFromTypeAndVersion(RobotType::UR10, polyscope_5_version), RobotSeries::E_SERIES);
106+
107+
EXPECT_EQ(robotSeriesFromTypeAndVersion(RobotType::UR3, polyscope_x_version), RobotSeries::E_SERIES);
108+
EXPECT_EQ(robotSeriesFromTypeAndVersion(RobotType::UR5, polyscope_x_version), RobotSeries::E_SERIES);
109+
EXPECT_EQ(robotSeriesFromTypeAndVersion(RobotType::UR10, polyscope_x_version), RobotSeries::E_SERIES);
110+
111+
// UR16: major >= 5 -> E_SERIES, otherwise UNDEFINED
112+
EXPECT_EQ(robotSeriesFromTypeAndVersion(RobotType::UR16, polyscope_5_version), RobotSeries::E_SERIES);
113+
EXPECT_EQ(robotSeriesFromTypeAndVersion(RobotType::UR16, polyscope_x_version), RobotSeries::E_SERIES);
114+
EXPECT_EQ(robotSeriesFromTypeAndVersion(RobotType::UR16, cb3_version), RobotSeries::UNDEFINED);
115+
116+
// UR15/UR18/UR20/UR30/UR8LONG: major >= 5 -> UR_SERIES, otherwise UNDEFINED
117+
EXPECT_EQ(robotSeriesFromTypeAndVersion(RobotType::UR15, polyscope_x_version), RobotSeries::UR_SERIES);
118+
EXPECT_EQ(robotSeriesFromTypeAndVersion(RobotType::UR18, polyscope_x_version), RobotSeries::UR_SERIES);
119+
EXPECT_EQ(robotSeriesFromTypeAndVersion(RobotType::UR20, polyscope_x_version), RobotSeries::UR_SERIES);
120+
EXPECT_EQ(robotSeriesFromTypeAndVersion(RobotType::UR30, polyscope_x_version), RobotSeries::UR_SERIES);
121+
EXPECT_EQ(robotSeriesFromTypeAndVersion(RobotType::UR8LONG, polyscope_x_version), RobotSeries::UR_SERIES);
122+
123+
EXPECT_EQ(robotSeriesFromTypeAndVersion(RobotType::UR15, polyscope_5_version), RobotSeries::UR_SERIES);
124+
EXPECT_EQ(robotSeriesFromTypeAndVersion(RobotType::UR18, polyscope_5_version), RobotSeries::UR_SERIES);
125+
EXPECT_EQ(robotSeriesFromTypeAndVersion(RobotType::UR20, polyscope_5_version), RobotSeries::UR_SERIES);
126+
EXPECT_EQ(robotSeriesFromTypeAndVersion(RobotType::UR30, polyscope_5_version), RobotSeries::UR_SERIES);
127+
EXPECT_EQ(robotSeriesFromTypeAndVersion(RobotType::UR8LONG, polyscope_5_version), RobotSeries::UR_SERIES);
128+
129+
EXPECT_EQ(robotSeriesFromTypeAndVersion(RobotType::UR15, cb3_version), RobotSeries::UNDEFINED);
130+
EXPECT_EQ(robotSeriesFromTypeAndVersion(RobotType::UR18, cb3_version), RobotSeries::UNDEFINED);
131+
EXPECT_EQ(robotSeriesFromTypeAndVersion(RobotType::UR20, cb3_version), RobotSeries::UNDEFINED);
132+
EXPECT_EQ(robotSeriesFromTypeAndVersion(RobotType::UR30, cb3_version), RobotSeries::UNDEFINED);
133+
EXPECT_EQ(robotSeriesFromTypeAndVersion(RobotType::UR8LONG, cb3_version), RobotSeries::UNDEFINED);
134+
135+
// UNDEFINED robot type yields UNDEFINED series
136+
EXPECT_EQ(robotSeriesFromTypeAndVersion(RobotType::UNDEFINED, polyscope_5_version), RobotSeries::UNDEFINED);
137+
EXPECT_EQ(robotSeriesFromTypeAndVersion(RobotType::UNDEFINED, cb3_version), RobotSeries::UNDEFINED);
138+
EXPECT_EQ(robotSeriesFromTypeAndVersion(RobotType::UNDEFINED, polyscope_x_version), RobotSeries::UNDEFINED);
139+
}

tests/test_primary_client.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,8 @@ TEST_F(PrimaryClientTest, test_uninitialized_primary_client)
197197
ASSERT_THROW(client_->isRobotProtectiveStopped(), UrException);
198198
// The client is not started yet, so the robot type should be UNDEFINED
199199
ASSERT_EQ(client_->getRobotType(), RobotType::UNDEFINED);
200+
// Without a robot type (and version), the robot series cannot be determined.
201+
ASSERT_EQ(client_->getRobotSeries(), RobotSeries::UNDEFINED);
200202
}
201203

202204
TEST_F(PrimaryClientTest, test_stop_command)
@@ -266,6 +268,34 @@ TEST_F(PrimaryClientTest, test_configuration_data)
266268
EXPECT_NE(client_->getRobotType(), RobotType::UNDEFINED);
267269
}
268270

271+
TEST_F(PrimaryClientTest, test_get_robot_series)
272+
{
273+
EXPECT_NO_THROW(client_->start());
274+
275+
// Wait until we have received configuration data so that the robot type is known.
276+
auto start_time = std::chrono::system_clock::now();
277+
const auto timeout = std::chrono::seconds(2);
278+
while (client_->getConfigurationData() == nullptr && std::chrono::system_clock::now() - start_time < timeout)
279+
{
280+
std::this_thread::sleep_for(std::chrono::milliseconds(100));
281+
}
282+
ASSERT_NE(client_->getConfigurationData(), nullptr);
283+
284+
const RobotType robot_type = client_->getRobotType();
285+
ASSERT_NE(robot_type, RobotType::UNDEFINED);
286+
287+
auto version_info = client_->getRobotVersion();
288+
ASSERT_NE(version_info, nullptr);
289+
290+
// The series returned by the client must match what robotSeriesFromTypeAndVersion computes
291+
// from the type and version the client itself reports.
292+
const RobotSeries expected_series = robotSeriesFromTypeAndVersion(robot_type, *version_info);
293+
EXPECT_EQ(client_->getRobotSeries(), expected_series);
294+
295+
// A robot we successfully connected to should map to a known series.
296+
EXPECT_NE(client_->getRobotSeries(), RobotSeries::UNDEFINED);
297+
}
298+
269299
TEST_F(PrimaryClientTest, test_program_execution)
270300
{
271301
auto consumer = std::make_shared<RobotMessageConsumer>();

0 commit comments

Comments
 (0)