Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[rcl_action] Add function to check if goal can be transitioned to CANCELING #325

Merged
merged 3 commits into from
Nov 13, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 28 additions & 5 deletions rcl_action/include/rcl_action/goal_handle.h
Original file line number Diff line number Diff line change
Expand Up @@ -202,15 +202,37 @@ rcl_action_goal_handle_get_status(
* Lock-Free | Yes
*
* \param[in] goal_handle struct containing the goal and metadata
* \return `true` if a goal is in one of the following states: ACCEPTED, EXECUTING, or CANCELING, or
* \return `false` otherwise, also
* \return `false` if the goal handle pointer is invalid
* \return `true` if the goal is in one of the following states: ACCEPTED, EXECUTING, or CANCELING, or
* \return `false` if the goal handle pointer is invalid, or
* \return `false` otherwise
*/
RCL_ACTION_PUBLIC
RCL_WARN_UNUSED
bool
rcl_action_goal_handle_is_active(const rcl_action_goal_handle_t * goal_handle);

/// Check if a goal can be transitioned to CANCELING in its current state.
/**
* This is a non-blocking call.
*
* <hr>
* Attribute | Adherence
* ------------------ | -------------
* Allocates Memory | No
* Thread-Safe | No
* Uses Atomics | No
* Lock-Free | Yes
*
* \param[in] goal_handle struct containing the goal and metadata
* \return `true` if the goal can be transitioned to CANCELING from its current state, or
* \return `false` if the goal handle pointer is invalid, or
* \return `false` otherwise
*/
RCL_ACTION_PUBLIC
RCL_WARN_UNUSED
bool
rcl_action_goal_handle_is_cancelable(const rcl_action_goal_handle_t * goal_handle);

/// Check if a rcl_action_goal_handle_t is valid.
/**
* This is a non-blocking call.
Expand All @@ -229,8 +251,9 @@ rcl_action_goal_handle_is_active(const rcl_action_goal_handle_t * goal_handle);
* Lock-Free | Yes
*
* \param[in] goal_handle struct to evaluate as valid or not
* \return `true` if the goal handle is valid, `false` otherwise, also
* \return `false` if the goal handle pointer is null
* \return `true` if the goal handle is valid, or
* \return `false` if the goal handle pointer is null, or
* \return `false` otherwise
*/
RCL_ACTION_PUBLIC
RCL_WARN_UNUSED
Expand Down
12 changes: 12 additions & 0 deletions rcl_action/src/rcl_action/goal_handle.c
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,18 @@ rcl_action_goal_handle_is_active(const rcl_action_goal_handle_t * goal_handle)
}
}

bool
rcl_action_goal_handle_is_cancelable(const rcl_action_goal_handle_t * goal_handle)
{
if (!rcl_action_goal_handle_is_valid(goal_handle)) {
return false; // error message is set
}
// Check if the state machine reports a cancel event is valid
rcl_action_goal_state_t state = rcl_action_transition_goal_state(
goal_handle->impl->state, GOAL_EVENT_CANCEL);
return GOAL_STATE_CANCELING == state;
}

bool
rcl_action_goal_handle_is_valid(const rcl_action_goal_handle_t * goal_handle)
{
Expand Down
88 changes: 48 additions & 40 deletions rcl_action/test/rcl_action/test_goal_handle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

#include <sstream>
#include <string>
#include <utility>
#include <tuple>
#include <vector>

#include "rcl_action/goal_handle.h"
Expand Down Expand Up @@ -156,8 +156,9 @@ TEST(TestGoalHandle, test_goal_handle_update_state_invalid)
rcl_reset_error();
}

using EventStatePair = std::pair<rcl_action_goal_event_t, rcl_action_goal_state_t>;
using StateTransitionSequence = std::vector<EventStatePair>;
using EventStateActiveCancelableTuple =
std::tuple<rcl_action_goal_event_t, rcl_action_goal_state_t, bool, bool>;
using StateTransitionSequence = std::vector<EventStateActiveCancelableTuple>;
const std::vector<std::string> event_strs = {
"EXECUTE", "CANCEL", "SET_SUCCEEDED", "SET_ABORTED", "SET_CANCELED"};

Expand All @@ -169,8 +170,8 @@ class TestGoalHandleStateTransitionSequence
const testing::TestParamInfo<StateTransitionSequence> & info)
{
std::stringstream result;
for (const EventStatePair & event_state : info.param) {
result << "_" << event_strs[event_state.first];
for (const EventStateActiveCancelableTuple & event_state : info.param) {
result << "_" << event_strs[std::get<0>(event_state)];
}
return result.str();
}
Expand Down Expand Up @@ -216,56 +217,63 @@ TEST_P(TestGoalHandleStateTransitionSequence, test_goal_handle_state_transitions

// Walk through state transitions
rcl_ret_t ret;
for (const EventStatePair & event_state : this->test_sequence) {
ret = rcl_action_update_goal_state(&this->goal_handle, event_state.first);
const rcl_action_goal_state_t & expected_state = event_state.second;
if (GOAL_STATE_UNKNOWN == expected_state) {
for (const EventStateActiveCancelableTuple & event_state : this->test_sequence) {
rcl_action_goal_event_t goal_event;
rcl_action_goal_state_t expected_goal_state;
bool expected_is_active;
bool expected_is_cancelable;
std::tie(goal_event, expected_goal_state, expected_is_active, expected_is_cancelable) =
event_state;
ret = rcl_action_update_goal_state(&this->goal_handle, goal_event);
if (GOAL_STATE_UNKNOWN == expected_goal_state) {
EXPECT_EQ(ret, RCL_RET_ACTION_GOAL_EVENT_INVALID);
continue;
}
EXPECT_EQ(ret, RCL_RET_OK);
expect_state_eq(expected_state);
expect_state_eq(expected_goal_state);
EXPECT_EQ(expected_is_active, rcl_action_goal_handle_is_active(&this->goal_handle));
EXPECT_EQ(expected_is_cancelable, rcl_action_goal_handle_is_cancelable(&this->goal_handle));
}
}

// Test sequence parameters
// Note, each sequence starts in the ACCEPTED state
const StateTransitionSequence valid_state_transition_sequences[] = {
{
{GOAL_EVENT_EXECUTE, GOAL_STATE_EXECUTING},
{GOAL_EVENT_CANCEL, GOAL_STATE_CANCELING},
{GOAL_EVENT_SET_CANCELED, GOAL_STATE_CANCELED},
std::make_tuple(GOAL_EVENT_EXECUTE, GOAL_STATE_EXECUTING, true, true),
std::make_tuple(GOAL_EVENT_CANCEL, GOAL_STATE_CANCELING, true, false),
std::make_tuple(GOAL_EVENT_SET_CANCELED, GOAL_STATE_CANCELED, false, false),
},
{
{GOAL_EVENT_EXECUTE, GOAL_STATE_EXECUTING},
{GOAL_EVENT_CANCEL, GOAL_STATE_CANCELING},
{GOAL_EVENT_SET_SUCCEEDED, GOAL_STATE_SUCCEEDED},
std::make_tuple(GOAL_EVENT_EXECUTE, GOAL_STATE_EXECUTING, true, true),
std::make_tuple(GOAL_EVENT_CANCEL, GOAL_STATE_CANCELING, true, false),
std::make_tuple(GOAL_EVENT_SET_SUCCEEDED, GOAL_STATE_SUCCEEDED, false, false),
},
{
{GOAL_EVENT_EXECUTE, GOAL_STATE_EXECUTING},
{GOAL_EVENT_CANCEL, GOAL_STATE_CANCELING},
{GOAL_EVENT_SET_ABORTED, GOAL_STATE_ABORTED},
std::make_tuple(GOAL_EVENT_EXECUTE, GOAL_STATE_EXECUTING, true, true),
std::make_tuple(GOAL_EVENT_CANCEL, GOAL_STATE_CANCELING, true, false),
std::make_tuple(GOAL_EVENT_SET_ABORTED, GOAL_STATE_ABORTED, false, false),
},
{
{GOAL_EVENT_EXECUTE, GOAL_STATE_EXECUTING},
{GOAL_EVENT_SET_SUCCEEDED, GOAL_STATE_SUCCEEDED},
std::make_tuple(GOAL_EVENT_EXECUTE, GOAL_STATE_EXECUTING, true, true),
std::make_tuple(GOAL_EVENT_SET_SUCCEEDED, GOAL_STATE_SUCCEEDED, false, false),
},
{
{GOAL_EVENT_EXECUTE, GOAL_STATE_EXECUTING},
{GOAL_EVENT_SET_ABORTED, GOAL_STATE_ABORTED},
std::make_tuple(GOAL_EVENT_EXECUTE, GOAL_STATE_EXECUTING, true, true),
std::make_tuple(GOAL_EVENT_SET_ABORTED, GOAL_STATE_ABORTED, false, false),
},
{
{GOAL_EVENT_CANCEL, GOAL_STATE_CANCELING},
{GOAL_EVENT_SET_CANCELED, GOAL_STATE_CANCELED},
std::make_tuple(GOAL_EVENT_CANCEL, GOAL_STATE_CANCELING, true, false),
std::make_tuple(GOAL_EVENT_SET_CANCELED, GOAL_STATE_CANCELED, false, false),
},
{
{GOAL_EVENT_CANCEL, GOAL_STATE_CANCELING},
{GOAL_EVENT_SET_ABORTED, GOAL_STATE_ABORTED},
std::make_tuple(GOAL_EVENT_CANCEL, GOAL_STATE_CANCELING, true, false),
std::make_tuple(GOAL_EVENT_SET_ABORTED, GOAL_STATE_ABORTED, false, false),
},
// This is an odd case, but valid nonetheless
{
{GOAL_EVENT_CANCEL, GOAL_STATE_CANCELING},
{GOAL_EVENT_SET_SUCCEEDED, GOAL_STATE_SUCCEEDED},
std::make_tuple(GOAL_EVENT_CANCEL, GOAL_STATE_CANCELING, true, false),
std::make_tuple(GOAL_EVENT_SET_SUCCEEDED, GOAL_STATE_SUCCEEDED, false, false),
},
};

Expand All @@ -277,27 +285,27 @@ INSTANTIATE_TEST_CASE_P(

const StateTransitionSequence invalid_state_transition_sequences[] = {
{
{GOAL_EVENT_EXECUTE, GOAL_STATE_EXECUTING},
{GOAL_EVENT_CANCEL, GOAL_STATE_CANCELING},
{GOAL_EVENT_EXECUTE, GOAL_STATE_UNKNOWN},
std::make_tuple(GOAL_EVENT_EXECUTE, GOAL_STATE_EXECUTING, true, true),
std::make_tuple(GOAL_EVENT_CANCEL, GOAL_STATE_CANCELING, true, false),
std::make_tuple(GOAL_EVENT_EXECUTE, GOAL_STATE_UNKNOWN, false, false),
},
{
{GOAL_EVENT_EXECUTE, GOAL_STATE_EXECUTING},
{GOAL_EVENT_CANCEL, GOAL_STATE_CANCELING},
{GOAL_EVENT_CANCEL, GOAL_STATE_UNKNOWN},
std::make_tuple(GOAL_EVENT_EXECUTE, GOAL_STATE_EXECUTING, true, true),
std::make_tuple(GOAL_EVENT_CANCEL, GOAL_STATE_CANCELING, true, false),
std::make_tuple(GOAL_EVENT_CANCEL, GOAL_STATE_UNKNOWN, false, false),
},
{
{GOAL_EVENT_EXECUTE, GOAL_STATE_EXECUTING},
{GOAL_EVENT_EXECUTE, GOAL_STATE_UNKNOWN},
std::make_tuple(GOAL_EVENT_EXECUTE, GOAL_STATE_EXECUTING, true, true),
std::make_tuple(GOAL_EVENT_EXECUTE, GOAL_STATE_UNKNOWN, false, false),
},
{
{GOAL_EVENT_SET_CANCELED, GOAL_STATE_UNKNOWN},
std::make_tuple(GOAL_EVENT_SET_CANCELED, GOAL_STATE_UNKNOWN, false, false),
},
{
{GOAL_EVENT_SET_SUCCEEDED, GOAL_STATE_UNKNOWN},
std::make_tuple(GOAL_EVENT_SET_SUCCEEDED, GOAL_STATE_UNKNOWN, false, false),
},
{
{GOAL_EVENT_SET_ABORTED, GOAL_STATE_UNKNOWN},
std::make_tuple(GOAL_EVENT_SET_ABORTED, GOAL_STATE_UNKNOWN, false, false),
},
};

Expand Down