Skip to content

Commit e76636f

Browse files
authored
Add missing robot types (UniversalRobots#513)
This consists of two commits: - Adding missing robot types to type string conversion - Removing default branches in helper function switch cases. This way, adding a new enum entry will raise a compiler warning, which we will treat as an error in CI. I pushed both commits into this PR, as for the type string conversion this is highly related / the cause why this wasn't detected earlier.
1 parent c85ffa8 commit e76636f

3 files changed

Lines changed: 180 additions & 20 deletions

File tree

include/ur_client_library/ur/datatypes.h

Lines changed: 13 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -144,11 +144,10 @@ inline std::string robotModeString(const RobotMode& mode)
144144
return "RUNNING";
145145
case RobotMode::UPDATING_FIRMWARE:
146146
return "UPDATING_FIRMWARE";
147-
default:
148-
std::stringstream ss;
149-
ss << "Unknown robot mode: " << static_cast<int>(mode);
150-
throw std::invalid_argument(ss.str());
147+
case RobotMode::UNKNOWN:
148+
return "UNKNOWN";
151149
}
150+
throw std::invalid_argument("Unknown robot mode: " + std::to_string(static_cast<int>(mode)));
152151
}
153152

154153
inline std::string safetyModeString(const SafetyMode& mode)
@@ -197,11 +196,8 @@ inline std::string safetyModeString(const SafetyMode& mode)
197196
return "PROFISAFE_EMERGENCY_STOP";
198197
case SafetyMode::SAFETY_API_SAFEGUARD_STOP:
199198
return "SAFETY_API_SAFEGUARD_STOP";
200-
default:
201-
std::stringstream ss;
202-
ss << "Unknown safety mode: " << static_cast<int>(mode);
203-
throw std::invalid_argument(ss.str());
204199
}
200+
throw std::invalid_argument("Unknown safety mode: " + std::to_string(static_cast<int>(mode)));
205201
}
206202

207203
inline std::string safetyStatusString(const SafetyStatus& status)
@@ -234,11 +230,8 @@ inline std::string safetyStatusString(const SafetyStatus& status)
234230
return "AUTOMATIC_MODE_SAFEGUARD_STOP";
235231
case SafetyStatus::SYSTEM_THREE_POSITION_ENABLING_STOP:
236232
return "SYSTEM_THREE_POSITION_ENABLING_STOP";
237-
default:
238-
std::stringstream ss;
239-
ss << "Unknown safety status: " << static_cast<int>(status);
240-
throw std::invalid_argument(ss.str());
241233
}
234+
throw std::invalid_argument("Unknown safety status: " + std::to_string(static_cast<int>(status)));
242235
}
243236

244237
inline std::string robotTypeString(const RobotType& type)
@@ -249,22 +242,24 @@ inline std::string robotTypeString(const RobotType& type)
249242
return "UR3";
250243
case RobotType::UR5:
251244
return "UR5";
245+
case RobotType::UR8LONG:
246+
return "UR8_LONG";
252247
case RobotType::UR10:
253248
return "UR10";
254249
case RobotType::UR15:
255250
return "UR15";
256251
case RobotType::UR16:
257252
return "UR16";
253+
case RobotType::UR18:
254+
return "UR18";
258255
case RobotType::UR20:
259256
return "UR20";
260257
case RobotType::UR30:
261258
return "UR30";
262-
default:
263-
std::stringstream ss;
264-
ss << "Unknown Robot Type: " << static_cast<int>(type);
265-
URCL_LOG_WARN(ss.str().c_str());
259+
case RobotType::UNDEFINED:
266260
return "UNDEFINED";
267261
}
262+
throw std::invalid_argument("Unknown robot type: " + std::to_string(static_cast<int>(type)));
268263
}
269264

270265
/**
@@ -286,12 +281,10 @@ inline std::string robotSeriesString(const RobotSeries& series)
286281
return "E_SERIES";
287282
case RobotSeries::UR_SERIES:
288283
return "UR_SERIES";
289-
default:
290-
std::stringstream ss;
291-
ss << "Unknown Robot Series: " << static_cast<int>(series);
292-
URCL_LOG_WARN(ss.str().c_str());
284+
case RobotSeries::UNDEFINED:
293285
return "UNDEFINED";
294286
}
287+
throw std::invalid_argument("Unknown robot series: " + std::to_string(static_cast<int>(series)));
295288
}
296289

297290
} // namespace urcl

tests/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,11 @@ target_link_libraries(types_tests PRIVATE ur_client_library::urcl GTest::gtest_m
289289
gtest_add_tests(TARGET types_tests
290290
)
291291

292+
add_executable(datatypes_tests test_datatypes.cpp)
293+
target_link_libraries(datatypes_tests PRIVATE ur_client_library::urcl GTest::gtest_main)
294+
gtest_add_tests(TARGET datatypes_tests
295+
)
296+
292297
add_executable(motion_primitives_tests test_motion_primitives.cpp)
293298
target_link_libraries(motion_primitives_tests PRIVATE ur_client_library::urcl GTest::gtest_main)
294299
gtest_add_tests(TARGET motion_primitives_tests

tests/test_datatypes.cpp

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
// -- BEGIN LICENSE BLOCK ----------------------------------------------
2+
// Copyright 2026 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+
33+
#include <cstdint>
34+
#include <stdexcept>
35+
#include <string>
36+
37+
#include <ur_client_library/ur/datatypes.h>
38+
39+
using namespace urcl;
40+
41+
TEST(TestDatatypes, RobotModeString_all_values)
42+
{
43+
EXPECT_EQ(robotModeString(RobotMode::UNKNOWN), "UNKNOWN");
44+
EXPECT_EQ(robotModeString(RobotMode::NO_CONTROLLER), "NO_CONTROLLER");
45+
EXPECT_EQ(robotModeString(RobotMode::DISCONNECTED), "DISCONNECTED");
46+
EXPECT_EQ(robotModeString(RobotMode::CONFIRM_SAFETY), "CONFIRM_SAFETY");
47+
EXPECT_EQ(robotModeString(RobotMode::BOOTING), "BOOTING");
48+
EXPECT_EQ(robotModeString(RobotMode::POWER_OFF), "POWER_OFF");
49+
EXPECT_EQ(robotModeString(RobotMode::POWER_ON), "POWER_ON");
50+
EXPECT_EQ(robotModeString(RobotMode::IDLE), "IDLE");
51+
EXPECT_EQ(robotModeString(RobotMode::BACKDRIVE), "BACKDRIVE");
52+
EXPECT_EQ(robotModeString(RobotMode::RUNNING), "RUNNING");
53+
EXPECT_EQ(robotModeString(RobotMode::UPDATING_FIRMWARE), "UPDATING_FIRMWARE");
54+
}
55+
56+
TEST(TestDatatypes, RobotModeString_invalid_throws)
57+
{
58+
const RobotMode invalid = static_cast<RobotMode>(99);
59+
EXPECT_THROW(robotModeString(invalid), std::invalid_argument);
60+
}
61+
62+
TEST(TestDatatypes, SafetyModeString_all_values)
63+
{
64+
EXPECT_EQ(safetyModeString(SafetyMode::NORMAL), "NORMAL");
65+
EXPECT_EQ(safetyModeString(SafetyMode::REDUCED), "REDUCED");
66+
EXPECT_EQ(safetyModeString(SafetyMode::PROTECTIVE_STOP), "PROTECTIVE_STOP");
67+
EXPECT_EQ(safetyModeString(SafetyMode::RECOVERY), "RECOVERY");
68+
EXPECT_EQ(safetyModeString(SafetyMode::SAFEGUARD_STOP), "SAFEGUARD_STOP");
69+
EXPECT_EQ(safetyModeString(SafetyMode::SYSTEM_EMERGENCY_STOP), "SYSTEM_EMERGENCY_STOP");
70+
EXPECT_EQ(safetyModeString(SafetyMode::ROBOT_EMERGENCY_STOP), "ROBOT_EMERGENCY_STOP");
71+
EXPECT_EQ(safetyModeString(SafetyMode::VIOLATION), "VIOLATION");
72+
EXPECT_EQ(safetyModeString(SafetyMode::FAULT), "FAULT");
73+
EXPECT_EQ(safetyModeString(SafetyMode::VALIDATE_JOINT_ID), "VALIDATE_JOINT_ID");
74+
EXPECT_EQ(safetyModeString(SafetyMode::UNDEFINED_SAFETY_MODE), "UNDEFINED_SAFETY_MODE");
75+
EXPECT_EQ(safetyModeString(SafetyMode::AUTOMATIC_MODE_SAFEGUARD_STOP), "AUTOMATIC_MODE_SAFEGUARD_STOP");
76+
EXPECT_EQ(safetyModeString(SafetyMode::SYSTEM_THREE_POSITION_ENABLING_STOP), "SYSTEM_THREE_POSITION_ENABLING_STOP");
77+
EXPECT_EQ(safetyModeString(SafetyMode::TP_THREE_POSITION_ENABLING_STOP), "TP_THREE_POSITION_ENABLING_STOP");
78+
EXPECT_EQ(safetyModeString(SafetyMode::IMMI_EMERGENCY_STOP), "IMMI_EMERGENCY_STOP");
79+
EXPECT_EQ(safetyModeString(SafetyMode::IMMI_SAFEGUARD_STOP), "IMMI_SAFEGUARD_STOP");
80+
EXPECT_EQ(safetyModeString(SafetyMode::PROFISAFE_WAITING_FOR_PARAMETERS), "PROFISAFE_WAITING_FOR_PARAMETERS");
81+
EXPECT_EQ(safetyModeString(SafetyMode::PROFISAFE_AUTOMATIC_MODE_SAFEGUARD_STOP), "PROFISAFE_AUTOMATIC_MODE_SAFEGUARD_"
82+
"STOP");
83+
EXPECT_EQ(safetyModeString(SafetyMode::PROFISAFE_SAFEGUARD_STOP), "PROFISAFE_SAFEGUARD_STOP");
84+
EXPECT_EQ(safetyModeString(SafetyMode::PROFISAFE_EMERGENCY_STOP), "PROFISAFE_EMERGENCY_STOP");
85+
EXPECT_EQ(safetyModeString(SafetyMode::SAFETY_API_SAFEGUARD_STOP), "SAFETY_API_SAFEGUARD_STOP");
86+
}
87+
88+
TEST(TestDatatypes, SafetyModeString_invalid_throws)
89+
{
90+
const SafetyMode invalid = static_cast<SafetyMode>(0);
91+
EXPECT_THROW(safetyModeString(invalid), std::invalid_argument);
92+
93+
const SafetyMode also_invalid = static_cast<SafetyMode>(250);
94+
EXPECT_THROW(safetyModeString(also_invalid), std::invalid_argument);
95+
}
96+
97+
TEST(TestDatatypes, SafetyStatusString_all_values)
98+
{
99+
EXPECT_EQ(safetyStatusString(SafetyStatus::NORMAL), "NORMAL");
100+
EXPECT_EQ(safetyStatusString(SafetyStatus::REDUCED), "REDUCED");
101+
EXPECT_EQ(safetyStatusString(SafetyStatus::PROTECTIVE_STOP), "PROTECTIVE_STOP");
102+
EXPECT_EQ(safetyStatusString(SafetyStatus::RECOVERY), "RECOVERY");
103+
EXPECT_EQ(safetyStatusString(SafetyStatus::SAFEGUARD_STOP), "SAFEGUARD_STOP");
104+
EXPECT_EQ(safetyStatusString(SafetyStatus::SYSTEM_EMERGENCY_STOP), "SYSTEM_EMERGENCY_STOP");
105+
EXPECT_EQ(safetyStatusString(SafetyStatus::ROBOT_EMERGENCY_STOP), "ROBOT_EMERGENCY_STOP");
106+
EXPECT_EQ(safetyStatusString(SafetyStatus::VIOLATION), "VIOLATION");
107+
EXPECT_EQ(safetyStatusString(SafetyStatus::FAULT), "FAULT");
108+
EXPECT_EQ(safetyStatusString(SafetyStatus::VALIDATE_JOINT_ID), "VALIDATE_JOINT_ID");
109+
EXPECT_EQ(safetyStatusString(SafetyStatus::UNDEFINED_SAFETY_MODE), "UNDEFINED_SAFETY_MODE");
110+
EXPECT_EQ(safetyStatusString(SafetyStatus::AUTOMATIC_MODE_SAFEGUARD_STOP), "AUTOMATIC_MODE_SAFEGUARD_STOP");
111+
EXPECT_EQ(safetyStatusString(SafetyStatus::SYSTEM_THREE_POSITION_ENABLING_STOP), "SYSTEM_THREE_POSITION_ENABLING_"
112+
"STOP");
113+
}
114+
115+
TEST(TestDatatypes, SafetyStatusString_invalid_throws)
116+
{
117+
const SafetyStatus invalid = static_cast<SafetyStatus>(0);
118+
EXPECT_THROW(safetyStatusString(invalid), std::invalid_argument);
119+
120+
const SafetyStatus also_invalid = static_cast<SafetyStatus>(99);
121+
EXPECT_THROW(safetyStatusString(also_invalid), std::invalid_argument);
122+
}
123+
124+
TEST(TestDatatypes, RobotTypeString_all_values)
125+
{
126+
EXPECT_EQ(robotTypeString(RobotType::UNDEFINED), "UNDEFINED");
127+
EXPECT_EQ(robotTypeString(RobotType::UR3), "UR3");
128+
EXPECT_EQ(robotTypeString(RobotType::UR5), "UR5");
129+
EXPECT_EQ(robotTypeString(RobotType::UR8LONG), "UR8_LONG");
130+
EXPECT_EQ(robotTypeString(RobotType::UR10), "UR10");
131+
EXPECT_EQ(robotTypeString(RobotType::UR15), "UR15");
132+
EXPECT_EQ(robotTypeString(RobotType::UR16), "UR16");
133+
EXPECT_EQ(robotTypeString(RobotType::UR18), "UR18");
134+
EXPECT_EQ(robotTypeString(RobotType::UR20), "UR20");
135+
EXPECT_EQ(robotTypeString(RobotType::UR30), "UR30");
136+
}
137+
138+
TEST(TestDatatypes, RobotTypeString_invalid_throws)
139+
{
140+
const RobotType invalid = static_cast<RobotType>(0);
141+
EXPECT_THROW(robotTypeString(invalid), std::invalid_argument);
142+
143+
const RobotType also_invalid = static_cast<RobotType>(99);
144+
EXPECT_THROW(robotTypeString(also_invalid), std::invalid_argument);
145+
}
146+
147+
TEST(TestDatatypes, RobotSeriesString_all_values)
148+
{
149+
EXPECT_EQ(robotSeriesString(RobotSeries::UNDEFINED), "UNDEFINED");
150+
EXPECT_EQ(robotSeriesString(RobotSeries::CB3), "CB3");
151+
EXPECT_EQ(robotSeriesString(RobotSeries::E_SERIES), "E_SERIES");
152+
EXPECT_EQ(robotSeriesString(RobotSeries::UR_SERIES), "UR_SERIES");
153+
}
154+
155+
TEST(TestDatatypes, RobotSeriesString_invalid_throws)
156+
{
157+
const RobotSeries invalid = static_cast<RobotSeries>(0);
158+
EXPECT_THROW(robotSeriesString(invalid), std::invalid_argument);
159+
160+
const RobotSeries also_invalid = static_cast<RobotSeries>(42);
161+
EXPECT_THROW(robotSeriesString(also_invalid), std::invalid_argument);
162+
}

0 commit comments

Comments
 (0)