From 1349be081a48f4a75102fffd12caa1fbf7a19f1b Mon Sep 17 00:00:00 2001 From: Felix Exner Date: Fri, 18 Sep 2026 13:28:39 +0200 Subject: [PATCH] Add support for g-series robots - RobotType definitions - RobotSeries definition - Startupscript arguments (preparation for 10.15.0) --- include/ur_client_library/helpers.h | 2 +- include/ur_client_library/ur/datatypes.h | 16 +++++++-- scripts/start_ursim.sh | 18 +++++++--- src/helpers.cpp | 33 ++++++++++++++--- tests/test_datatypes.cpp | 7 ++++ tests/test_helpers.cpp | 22 ++++++++++++ tests/test_primary_parser.cpp | 43 +++++++++++++--------- tests/test_start_ursim.bats | 45 ++++++++++++++++++++++++ 8 files changed, 157 insertions(+), 29 deletions(-) diff --git a/include/ur_client_library/helpers.h b/include/ur_client_library/helpers.h index 10590cd12..26b91acbe 100644 --- a/include/ur_client_library/helpers.h +++ b/include/ur_client_library/helpers.h @@ -178,7 +178,7 @@ RobotSeries robotSeriesFromTypeAndVersion(const RobotType type, const VersionInf * * \param robot_type_str The string representation of the robot type as used in the start_ursim.sh * script. Must be all lower-case, e.g. "ur3e", "ur5", "ur10e", "ur16e", "ur7e", "ur15", "ur30", - * "ur8long". + * "ur8long", "ur10g-1750", "ur17g-1300", "ur18g-950". * * \throws std::invalid_argument if \p robot_type_str does not match a known robot type. * diff --git a/include/ur_client_library/ur/datatypes.h b/include/ur_client_library/ur/datatypes.h index efd7784cb..f6f931351 100644 --- a/include/ur_client_library/ur/datatypes.h +++ b/include/ur_client_library/ur/datatypes.h @@ -109,7 +109,10 @@ enum class RobotType : int32_t UR8LONG = 6, UR20 = 7, UR30 = 8, - UR15 = 9 + UR15 = 9, + UR10G_1750 = 12, + UR17G_1300 = 13, + UR18G_950 = 14 }; enum class RobotSeries @@ -117,7 +120,8 @@ enum class RobotSeries UNDEFINED = -128, CB3 = 1, E_SERIES = 2, - UR_SERIES = 3 + UR_SERIES = 3, + G_SERIES = 4 }; enum class ReportLevel : int32_t @@ -318,6 +322,12 @@ inline std::string robotTypeString(const RobotType& type) return "UR20"; case RobotType::UR30: return "UR30"; + case RobotType::UR10G_1750: + return "UR10g-1750"; + case RobotType::UR17G_1300: + return "UR17g-1300"; + case RobotType::UR18G_950: + return "UR18g-950"; case RobotType::UNDEFINED: return "UNDEFINED"; } @@ -343,6 +353,8 @@ inline std::string robotSeriesString(const RobotSeries& series) return "E_SERIES"; case RobotSeries::UR_SERIES: return "UR_SERIES"; + case RobotSeries::G_SERIES: + return "G_SERIES"; case RobotSeries::UNDEFINED: return "UNDEFINED"; } diff --git a/scripts/start_ursim.sh b/scripts/start_ursim.sh index 0f9b50b1e..977fb16cf 100755 --- a/scripts/start_ursim.sh +++ b/scripts/start_ursim.sh @@ -43,7 +43,7 @@ help() echo echo "Syntax: `basename "$0"` [-m|s|h]" echo "options:" - echo " -m Robot model. One of [ur3, ur3e, ur5, ur5e, ur7e, ur8long, ur10e, ur12e, ur16e, ur15, ur18, ur20, ur30]. Defaults to ur5e." + echo " -m Robot model. One of [ur3, ur3e, ur5, ur5e, ur7e, ur8long, ur10e, ur12e, ur16e, ur15, ur18, ur20, ur30, ur10g-1750, ur17g-1300, ur18g-950]. Defaults to ur5e." echo " -v URSim version that should be used. See https://hub.docker.com/r/universalrobots/ursim_e-series/tags for available versions. Defaults to 'latest'" @@ -97,6 +97,9 @@ get_series_from_model() ur8long|ur15|ur18|ur20|ur30) ROBOT_SERIES=e-series ;; + ur10g-1750|ur17g-1300|ur18g-950) + ROBOT_SERIES=polyscopex + ;; *) echo "Not a valid robot model: $robot_model" exit 1 @@ -133,7 +136,7 @@ strip_robot_model() ROBOT_MODEL=${robot_model^^} else ROBOT_MODEL=${robot_model^^} - # UR8LONG, UR15, UR18, UR20 and UR30 need no further adjustment + # UR8LONG, UR15, UR18, UR20, UR30 and g-Series models need no further adjustment if [[ "$robot_model" = @(ur3e|ur5e|ur10e|ur16e) ]]; then ROBOT_MODEL=$(echo "${ROBOT_MODEL:0:$((${#ROBOT_MODEL}-1))}") elif [[ "$robot_model" = @(ur7e|ur12e) ]]; then @@ -165,6 +168,7 @@ validate_parameters() local MIN_UR7e_X="10.9.0" # and UR12e local MIN_UR8LONG="5.23.0" # and UR18 local MIN_UR8LONG_X="10.11.0" # and UR18 + local MIN_G_SERIES="10.15.0" local URSIM_VERSION_CHECK="$URSIM_VERSION" if [[ "$URSIM_VERSION" == "latest" ]]; then @@ -173,7 +177,11 @@ validate_parameters() elif [[ "$ROBOT_SERIES" == "e-series" ]]; then URSIM_VERSION_CHECK="$MIN_UR15" elif [[ "$ROBOT_SERIES" == "polyscopex" ]]; then - URSIM_VERSION_CHECK="MIN_UR15_X" + if [[ $ROBOT_MODEL == @(ur10g-1750|ur17g-1300|ur18g-950) ]]; then + URSIM_VERSION_CHECK="$MIN_G_SERIES" + else + URSIM_VERSION_CHECK="$MIN_UR15_X" + fi fi fi @@ -216,8 +224,10 @@ validate_parameters() echo "PolyscopeX is only supported from version $MIN_POLYSCOPE_X onwards" exit 1 fi - if [[ $ROBOT_MODEL != @(ur3e|ur5e|ur7e|ur8long|ur10e|ur12e|ur16e|ur15|ur18|ur20|ur30) ]]; then + if [[ $ROBOT_MODEL != @(ur3e|ur5e|ur7e|ur8long|ur10e|ur12e|ur16e|ur15|ur18|ur20|ur30|ur10g-1750|ur17g-1300|ur18g-950) ]]; then echo "$ROBOT_MODEL is no valid PolyscopeX model!" && exit 1 + elif [[ $ROBOT_MODEL == @(ur10g-1750|ur17g-1300|ur18g-950) ]]; then + MIN_VERSION=$MIN_G_SERIES elif [[ $ROBOT_MODEL == "ur7e" || $ROBOT_MODEL == "ur12e" ]]; then MIN_VERSION=$MIN_UR7e_X elif [[ $ROBOT_MODEL == "ur8long" || $ROBOT_MODEL == "ur18" ]]; then diff --git a/src/helpers.cpp b/src/helpers.cpp index b8f33588c..5402d7f6c 100644 --- a/src/helpers.cpp +++ b/src/helpers.cpp @@ -199,6 +199,17 @@ RobotSeries robotSeriesFromTypeAndVersion(const RobotType type, const VersionInf { return RobotSeries::UNDEFINED; } + case RobotType::UR10G_1750: + case RobotType::UR17G_1300: + case RobotType::UR18G_950: + if (version_info.major > 10 || (version_info.major == 10 && version_info.minor >= 15)) + { + return RobotSeries::G_SERIES; + } + else + { + return RobotSeries::UNDEFINED; + } case RobotType::UNDEFINED: return RobotSeries::UNDEFINED; } @@ -210,11 +221,23 @@ RobotType robotTypeFromString(const std::string& robot_type_str) // RobotType has no dedicated entries for UR7/UR12, so UR7e and UR12e are mapped to their // closest siblings UR5 and UR10 respectively, matching what the robot reports over primary. static const std::unordered_map string_to_robot_type{ - { "ur3", RobotType::UR3 }, { "ur3e", RobotType::UR3 }, { "ur5", RobotType::UR5 }, - { "ur5e", RobotType::UR5 }, { "ur7e", RobotType::UR5 }, { "ur10", RobotType::UR10 }, - { "ur10e", RobotType::UR10 }, { "ur12e", RobotType::UR10 }, { "ur16e", RobotType::UR16 }, - { "ur15", RobotType::UR15 }, { "ur18", RobotType::UR18 }, { "ur20", RobotType::UR20 }, - { "ur30", RobotType::UR30 }, { "ur8long", RobotType::UR8LONG }, + { "ur3", RobotType::UR3 }, + { "ur3e", RobotType::UR3 }, + { "ur5", RobotType::UR5 }, + { "ur5e", RobotType::UR5 }, + { "ur7e", RobotType::UR5 }, + { "ur10", RobotType::UR10 }, + { "ur10e", RobotType::UR10 }, + { "ur12e", RobotType::UR10 }, + { "ur16e", RobotType::UR16 }, + { "ur15", RobotType::UR15 }, + { "ur18", RobotType::UR18 }, + { "ur20", RobotType::UR20 }, + { "ur30", RobotType::UR30 }, + { "ur8long", RobotType::UR8LONG }, + { "ur10g-1750", RobotType::UR10G_1750 }, + { "ur17g-1300", RobotType::UR17G_1300 }, + { "ur18g-950", RobotType::UR18G_950 }, }; const auto it = string_to_robot_type.find(robot_type_str); diff --git a/tests/test_datatypes.cpp b/tests/test_datatypes.cpp index 5847772b7..52bf506cd 100644 --- a/tests/test_datatypes.cpp +++ b/tests/test_datatypes.cpp @@ -133,6 +133,9 @@ TEST(TestDatatypes, RobotTypeString_all_values) EXPECT_EQ(robotTypeString(RobotType::UR18), "UR18"); EXPECT_EQ(robotTypeString(RobotType::UR20), "UR20"); EXPECT_EQ(robotTypeString(RobotType::UR30), "UR30"); + EXPECT_EQ(robotTypeString(RobotType::UR10G_1750), "UR10g-1750"); + EXPECT_EQ(robotTypeString(RobotType::UR17G_1300), "UR17g-1300"); + EXPECT_EQ(robotTypeString(RobotType::UR18G_950), "UR18g-950"); } TEST(TestDatatypes, RobotTypeString_invalid_throws) @@ -140,6 +143,9 @@ TEST(TestDatatypes, RobotTypeString_invalid_throws) const RobotType invalid = static_cast(0); EXPECT_THROW(robotTypeString(invalid), std::invalid_argument); + EXPECT_THROW(robotTypeString(static_cast(10)), std::invalid_argument); + EXPECT_THROW(robotTypeString(static_cast(11)), std::invalid_argument); + const RobotType also_invalid = static_cast(99); EXPECT_THROW(robotTypeString(also_invalid), std::invalid_argument); } @@ -150,6 +156,7 @@ TEST(TestDatatypes, RobotSeriesString_all_values) EXPECT_EQ(robotSeriesString(RobotSeries::CB3), "CB3"); EXPECT_EQ(robotSeriesString(RobotSeries::E_SERIES), "E_SERIES"); EXPECT_EQ(robotSeriesString(RobotSeries::UR_SERIES), "UR_SERIES"); + EXPECT_EQ(robotSeriesString(RobotSeries::G_SERIES), "G_SERIES"); } TEST(TestDatatypes, RobotSeriesString_invalid_throws) diff --git a/tests/test_helpers.cpp b/tests/test_helpers.cpp index 658f4abb1..00573cd5b 100644 --- a/tests/test_helpers.cpp +++ b/tests/test_helpers.cpp @@ -132,6 +132,16 @@ TEST(TestHelpers, robotSeriesFromTypeAndVersion) EXPECT_EQ(robotSeriesFromTypeAndVersion(RobotType::UR30, cb3_version), RobotSeries::UNDEFINED); EXPECT_EQ(robotSeriesFromTypeAndVersion(RobotType::UR8LONG, cb3_version), RobotSeries::UNDEFINED); + // g-Series: version >= 10.15.0 -> G_SERIES, otherwise UNDEFINED + const VersionInformation before_g_series = VersionInformation::fromString("10.14.99.0"); + const VersionInformation first_g_series = VersionInformation::fromString("10.15.0.0"); + EXPECT_EQ(robotSeriesFromTypeAndVersion(RobotType::UR10G_1750, before_g_series), RobotSeries::UNDEFINED); + EXPECT_EQ(robotSeriesFromTypeAndVersion(RobotType::UR17G_1300, before_g_series), RobotSeries::UNDEFINED); + EXPECT_EQ(robotSeriesFromTypeAndVersion(RobotType::UR18G_950, before_g_series), RobotSeries::UNDEFINED); + EXPECT_EQ(robotSeriesFromTypeAndVersion(RobotType::UR10G_1750, first_g_series), RobotSeries::G_SERIES); + EXPECT_EQ(robotSeriesFromTypeAndVersion(RobotType::UR17G_1300, first_g_series), RobotSeries::G_SERIES); + EXPECT_EQ(robotSeriesFromTypeAndVersion(RobotType::UR18G_950, first_g_series), RobotSeries::G_SERIES); + // UNDEFINED robot type yields UNDEFINED series EXPECT_EQ(robotSeriesFromTypeAndVersion(RobotType::UNDEFINED, polyscope_5_version), RobotSeries::UNDEFINED); EXPECT_EQ(robotSeriesFromTypeAndVersion(RobotType::UNDEFINED, cb3_version), RobotSeries::UNDEFINED); @@ -143,9 +153,21 @@ TEST(TestHelpers, robotSeriesString) EXPECT_EQ(robotSeriesString(RobotSeries::CB3), "CB3"); EXPECT_EQ(robotSeriesString(RobotSeries::E_SERIES), "E_SERIES"); EXPECT_EQ(robotSeriesString(RobotSeries::UR_SERIES), "UR_SERIES"); + EXPECT_EQ(robotSeriesString(RobotSeries::G_SERIES), "G_SERIES"); EXPECT_EQ(robotSeriesString(RobotSeries::UNDEFINED), "UNDEFINED"); } +TEST(TestHelpers, robotTypeFromString) +{ + EXPECT_EQ(robotTypeFromString("ur10g-1750"), RobotType::UR10G_1750); + EXPECT_EQ(robotTypeFromString("ur17g-1300"), RobotType::UR17G_1300); + EXPECT_EQ(robotTypeFromString("ur18g-950"), RobotType::UR18G_950); + EXPECT_THROW(robotTypeFromString("ur10g"), std::invalid_argument); + EXPECT_THROW(robotTypeFromString("ur17g"), std::invalid_argument); + EXPECT_THROW(robotTypeFromString("ur18g"), std::invalid_argument); + EXPECT_THROW(robotTypeFromString("ur10g_1750"), std::invalid_argument); +} + TEST(TestHelpers, stringFromMotionTarget) { const auto q = Q{ 0.0, 0.1, 0.2, 0.3, 0.4, 0.5 }; diff --git a/tests/test_primary_parser.cpp b/tests/test_primary_parser.cpp index b6c846115..779989fce 100644 --- a/tests/test_primary_parser.cpp +++ b/tests/test_primary_parser.cpp @@ -465,24 +465,33 @@ TEST_F(PrimaryParserTest, parse_version_message) TEST_F(PrimaryParserTest, parse_hardware_info_message) { - unsigned char raw_data[sizeof(HARDWARE_INFO_MESSAGE)]; - memcpy(raw_data, HARDWARE_INFO_MESSAGE, sizeof(HARDWARE_INFO_MESSAGE)); - comm::BinParser bp(raw_data, sizeof(raw_data)); - - std::unique_ptr product; - ASSERT_TRUE(parser_.parse(bp, product)); - ASSERT_NE(product, nullptr); + const auto parse_and_check = [this](const uint8_t robot_type_id, const RobotType expected_robot_type) { + constexpr size_t ROBOT_TYPE_ID_OFFSET = 18; + unsigned char raw_data[sizeof(HARDWARE_INFO_MESSAGE)]; + memcpy(raw_data, HARDWARE_INFO_MESSAGE, sizeof(HARDWARE_INFO_MESSAGE)); + raw_data[ROBOT_TYPE_ID_OFFSET] = robot_type_id; + comm::BinParser bp(raw_data, sizeof(raw_data)); - auto* data = dynamic_cast(product.get()); - ASSERT_NE(data, nullptr); - EXPECT_EQ(data->timestamp_, 42); - EXPECT_EQ(data->source_, -2); - EXPECT_EQ(data->message_type_, primary_interface::RobotMessagePackageType::ROBOT_MESSAGE_HARDWARE_INFO); - EXPECT_EQ(data->robot_type_, RobotType::UR20); - EXPECT_EQ(data->reserved_1_, 0); - EXPECT_EQ(data->control_box_type_, ControlBoxType::CB7); - EXPECT_EQ(data->reserved_2_, 0); - EXPECT_EQ(data->tool_flange_type_, ToolFlangeType::V1); + std::unique_ptr product; + ASSERT_TRUE(parser_.parse(bp, product)); + ASSERT_NE(product, nullptr); + + auto* data = dynamic_cast(product.get()); + ASSERT_NE(data, nullptr); + EXPECT_EQ(data->timestamp_, 42); + EXPECT_EQ(data->source_, -2); + EXPECT_EQ(data->message_type_, primary_interface::RobotMessagePackageType::ROBOT_MESSAGE_HARDWARE_INFO); + EXPECT_EQ(data->robot_type_, expected_robot_type); + EXPECT_EQ(data->reserved_1_, 0); + EXPECT_EQ(data->control_box_type_, ControlBoxType::CB7); + EXPECT_EQ(data->reserved_2_, 0); + EXPECT_EQ(data->tool_flange_type_, ToolFlangeType::V1); + }; + + parse_and_check(7, RobotType::UR20); + parse_and_check(12, RobotType::UR10G_1750); + parse_and_check(13, RobotType::UR17G_1300); + parse_and_check(14, RobotType::UR18G_950); } TEST_F(PrimaryParserTest, parse_key_message) diff --git a/tests/test_start_ursim.bats b/tests/test_start_ursim.bats index 27539c4f5..5c2e802a0 100644 --- a/tests/test_start_ursim.bats +++ b/tests/test_start_ursim.bats @@ -56,6 +56,23 @@ setup() { echo "ROBOT_SERIES: $ROBOT_SERIES" [ "$ROBOT_SERIES" = "e-series" ] + get_series_from_model "ur10g-1750" + echo "ROBOT_SERIES: $ROBOT_SERIES" + [ "$ROBOT_SERIES" = "polyscopex" ] + + get_series_from_model "ur17g-1300" + echo "ROBOT_SERIES: $ROBOT_SERIES" + [ "$ROBOT_SERIES" = "polyscopex" ] + + get_series_from_model "ur18g-950" + echo "ROBOT_SERIES: $ROBOT_SERIES" + [ "$ROBOT_SERIES" = "polyscopex" ] + + for model in ur10g ur17g ur18g; do + run get_series_from_model "$model" + [ $status -eq 1 ] + done + run get_series_from_model "notarobotname" [ $status -eq 1 ] } @@ -283,6 +300,26 @@ setup() { [ $status -eq 0 ] } +@test "test g-series min version" { + for model in ur10g-1750 ur17g-1300 ur18g-950; do + run test_input_handling -m "$model" -v 5.25.1 + echo "$output" + [ $status -eq 1 ] + + run test_input_handling -m "$model" -v 10.14.9 + echo "$output" + [ $status -eq 1 ] + + run test_input_handling -m "$model" -v 10.15.0 + echo "$output" + [ $status -eq 0 ] + + run test_input_handling -m "$model" + echo "$output" + [ $status -eq 0 ] + done +} + @test "unsupported versions raise error" { run main -v 1.2.3 -t echo "$output" @@ -382,6 +419,14 @@ setup() { [ $status -eq 0 ] model=$(echo "$output" | tail -n1 | grep -Po '-e ROBOT_TYPE=\w+' | cut -d '=' -f2) [ "$model" == "UR30" ] + + for robot_model in ur10g-1750 ur17g-1300 ur18g-950; do + run main -m "$robot_model" -v 10.15.0 -t + echo "$output" + [ $status -eq 0 ] + model=$(echo "$output" | tail -n1 | grep -Po -- '-e ROBOT_TYPE=\S+' | cut -d '=' -f2) + [ "$model" == "${robot_model^^}" ] + done } @test "strip_robot_model" {