Skip to content

Commit 07d47ce

Browse files
dv-picknikclaude
andcommitted
Return GoalStatusCode::UNKNOWN for out-of-range goal status
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: David Vadovszki <david.vadovszki@picknik.ai>
1 parent 8604591 commit 07d47ce

6 files changed

Lines changed: 83 additions & 2 deletions

File tree

‎rclcpp_action/include/rclcpp_action/client_goal_handle.hpp‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,10 @@ class ClientGoalHandle
9595
get_status();
9696

9797
/// Get the goal status code as a GoalStatusCode.
98+
/**
99+
* \return GoalStatusCode::UNKNOWN if the server sent a status that is not one of the
100+
* action_msgs::msg::GoalStatus values. get_status() still returns the raw value.
101+
*/
98102
GoalStatusCode
99103
get_status_code();
100104

‎rclcpp_action/include/rclcpp_action/client_goal_handle_impl.hpp‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,11 @@ template<typename ActionT>
107107
GoalStatusCode
108108
ClientGoalHandle<ActionT>::get_status_code()
109109
{
110-
return static_cast<GoalStatusCode>(get_status());
110+
const int8_t status = get_status();
111+
if (status < GoalStatus::STATUS_UNKNOWN || status > GoalStatus::STATUS_ABORTED) {
112+
return GoalStatusCode::UNKNOWN;
113+
}
114+
return static_cast<GoalStatusCode>(status);
111115
}
112116

113117
template<typename ActionT>

‎rclcpp_action/include/rclcpp_action/generic_client_goal_handle.hpp‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,10 @@ class GenericClientGoalHandle
8787
get_status();
8888

8989
/// Get the goal status code as a GoalStatusCode.
90+
/**
91+
* \return GoalStatusCode::UNKNOWN if the server sent a status that is not one of the
92+
* action_msgs::msg::GoalStatus values. get_status() still returns the raw value.
93+
*/
9094
RCLCPP_ACTION_PUBLIC
9195
GoalStatusCode
9296
get_status_code();

‎rclcpp_action/src/generic_client_goal_handle.cpp‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,11 @@ GenericClientGoalHandle::get_status()
9292
GoalStatusCode
9393
GenericClientGoalHandle::get_status_code()
9494
{
95-
return static_cast<GoalStatusCode>(get_status());
95+
const int8_t status = get_status();
96+
if (status < GoalStatus::STATUS_UNKNOWN || status > GoalStatus::STATUS_ABORTED) {
97+
return GoalStatusCode::UNKNOWN;
98+
}
99+
return static_cast<GoalStatusCode>(status);
96100
}
97101

98102
void

‎rclcpp_action/test/test_client.cpp‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -667,6 +667,37 @@ TEST_F(TestClientAgainstServer, async_send_goal_no_callbacks_then_invalidate)
667667
EXPECT_THROW(future_result.get(), UnawareGoalHandleError);
668668
}
669669

670+
TEST_F(TestClientAgainstServer, get_status_code_out_of_range)
671+
{
672+
auto action_client = rclcpp_action::create_client<ActionType>(client_node, action_name);
673+
ASSERT_TRUE(action_client->wait_for_action_server(WAIT_FOR_SERVER_TIMEOUT));
674+
675+
ActionGoal goal;
676+
goal.order = 5;
677+
auto future_goal_handle = action_client->async_send_goal(goal);
678+
dual_spin_until_future_complete(future_goal_handle);
679+
auto goal_handle = future_goal_handle.get();
680+
ASSERT_NE(nullptr, goal_handle);
681+
682+
// A server that does not follow the action protocol can send any int8_t as a status.
683+
const int8_t out_of_range_status = 42;
684+
ActionStatusMessage status_message;
685+
rclcpp_action::GoalStatus goal_status;
686+
goal_status.goal_info.goal_id.uuid = goal_handle->get_goal_id();
687+
goal_status.status = out_of_range_status;
688+
status_message.status_list.push_back(goal_status);
689+
status_publisher->publish(status_message);
690+
691+
const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(5);
692+
while (goal_handle->get_status() != out_of_range_status &&
693+
std::chrono::steady_clock::now() < deadline)
694+
{
695+
client_executor.spin_some();
696+
}
697+
ASSERT_EQ(out_of_range_status, goal_handle->get_status());
698+
EXPECT_EQ(rclcpp_action::GoalStatusCode::UNKNOWN, goal_handle->get_status_code());
699+
}
700+
670701
TEST_F(TestClientAgainstServer, async_send_goal_with_goal_response_callback_wait_for_result)
671702
{
672703
auto action_client = rclcpp_action::create_client<ActionType>(client_node, action_name);

‎rclcpp_action/test/test_generic_client.cpp‎

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,40 @@ TEST_F(TestGenericClientAgainstServer, async_send_goal_no_callbacks_then_invalid
602602
EXPECT_THROW(future_result.get(), UnawareGoalHandleError);
603603
}
604604

605+
TEST_F(TestGenericClientAgainstServer, get_status_code_out_of_range)
606+
{
607+
auto action_generic_client = rclcpp_action::create_generic_client(
608+
client_node,
609+
action_name,
610+
"test_msgs/action/Fibonacci");
611+
ASSERT_TRUE(action_generic_client->wait_for_action_server(WAIT_FOR_SERVER_TIMEOUT));
612+
613+
ActionGoal goal;
614+
goal.order = 5;
615+
auto future_goal_handle = action_generic_client->async_send_goal(&goal, sizeof(goal));
616+
dual_spin_until_future_complete(future_goal_handle);
617+
auto goal_handle = future_goal_handle.get();
618+
ASSERT_NE(nullptr, goal_handle);
619+
620+
// A server that does not follow the action protocol can send any int8_t as a status.
621+
const int8_t out_of_range_status = 42;
622+
ActionStatusMessage status_message;
623+
rclcpp_action::GoalStatus goal_status;
624+
goal_status.goal_info.goal_id.uuid = goal_handle->get_goal_id();
625+
goal_status.status = out_of_range_status;
626+
status_message.status_list.push_back(goal_status);
627+
status_publisher->publish(status_message);
628+
629+
const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(5);
630+
while (goal_handle->get_status() != out_of_range_status &&
631+
std::chrono::steady_clock::now() < deadline)
632+
{
633+
client_executor.spin_some();
634+
}
635+
ASSERT_EQ(out_of_range_status, goal_handle->get_status());
636+
EXPECT_EQ(rclcpp_action::GoalStatusCode::UNKNOWN, goal_handle->get_status_code());
637+
}
638+
605639
TEST_F(TestGenericClientAgainstServer, async_send_goal_with_goal_response_callback_wait_for_result)
606640
{
607641
auto action_generic_client = rclcpp_action::create_generic_client(

0 commit comments

Comments
 (0)