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

[rclcpp_action] Action client implementation #594

Merged
merged 56 commits into from
Dec 5, 2018
Merged
Show file tree
Hide file tree
Changes from 50 commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
ecf8b5f
WIP
hidmic Nov 20, 2018
ad227d4
Added basic waitable interface to action client
sservulo Nov 26, 2018
584ab25
Updated waitable execute from action client
sservulo Nov 26, 2018
e221967
Added throw for rcl calls in action client
sservulo Nov 26, 2018
ce1b068
Removed duplicated ready flags from action client
sservulo Nov 26, 2018
ffcb8bb
Minor fix
sservulo Nov 26, 2018
d888fd5
Added header to action ClientBaseImpl execute
sservulo Nov 26, 2018
440ee03
Mich's update to action client interface
sservulo Nov 27, 2018
0c7dfd8
Removed async_cancel from action ClintGoalHandle API
sservulo Nov 22, 2018
a94ca35
Added status handler to action client goal handler
sservulo Nov 22, 2018
7fbb253
Added result handler to action client goal handler
sservulo Nov 22, 2018
1a1f79a
Identation fix
sservulo Nov 22, 2018
17a7a8d
Added get/set for action client goal handler
sservulo Nov 22, 2018
bcf1553
Changed action client goal handler attrs from rcl to cpp versions
sservulo Nov 22, 2018
5b2bc4c
Added check methods to action client goal handler
sservulo Nov 22, 2018
6b3482f
Removed rcl_client pointer from action client goal handler
sservulo Nov 22, 2018
5967df8
Added trailing suffix to client pimpl attrs
sservulo Nov 27, 2018
58215f3
Towards a consistent action client
hidmic Nov 28, 2018
1e9d56e
Misc fixes for the action client
hidmic Nov 28, 2018
5ec7562
Yet more misc fixes for the action client
hidmic Nov 28, 2018
68a2e17
Few more fixes and shortcuts to deal with missing type support.
hidmic Nov 28, 2018
4275ce7
Fixed lint errors in action headers and client
hidmic Nov 29, 2018
9545a0b
Fixes to action client internal workflow.
hidmic Nov 29, 2018
ab5df90
Misc fixes to get client example to build
sloretz Nov 30, 2018
4825f95
More misck client fixes
sloretz Nov 30, 2018
f04a50e
Remove debug print
sloretz Nov 30, 2018
750e19b
replace logging with throw_from_rcl_error
sloretz Dec 1, 2018
af831b3
Wrap result object given by client to user
sloretz Dec 1, 2018
da9d530
Fix a couple bugs trying to cancel goals
sloretz Dec 1, 2018
65fa041
Use unique_indentifier_msgs
sloretz Dec 4, 2018
2faa4b7
create_client accepts group and removes waitable
sloretz Dec 4, 2018
7fa9a14
Uncrustify fixes
sloretz Dec 4, 2018
7c2c69e
[rclcpp_action] Adds tests for action client.
sservulo Nov 28, 2018
927115c
[WIP] Failing action client tests.
hidmic Dec 5, 2018
c2c6775
[rclcpp_action] Action client tests passing.
hidmic Dec 5, 2018
ed1af61
Spin both executors to make tests pass on my machine
sloretz Dec 5, 2018
923564b
Feedback callback uses shared pointer
sloretz Dec 5, 2018
9e74ca4
comment about why make_result_aware is called
sloretz Dec 5, 2018
f7a52eb
Client documentation
sloretz Dec 5, 2018
8b173d5
Execute one thing at a time
sloretz Dec 5, 2018
f1ed490
Return nullptr instead of throwing RejectedGoalError
sloretz Dec 5, 2018
4dc00d9
ClientGoalHandle worries about feedback awareness
sloretz Dec 5, 2018
7489e75
cpplint + uncrustify
sloretz Dec 5, 2018
f2dc2ab
Use node logging interface
sloretz Dec 5, 2018
8e0da8f
ACTION -> ActionT
sloretz Dec 5, 2018
62c27a4
Make ClientBase constructor protected
sloretz Dec 5, 2018
cc2d038
Return types on different line
sloretz Dec 5, 2018
5797e51
Avoid passing const reference to temporary
sloretz Dec 5, 2018
df0a3fa
Child logger rclcpp_action
sloretz Dec 5, 2018
fa391a1
Child logger rclcpp_action
sloretz Dec 5, 2018
175213e
possible windows fixes
sloretz Dec 5, 2018
7cb39d8
remove excess space
sloretz Dec 5, 2018
05cfd8e
swap argument order
sloretz Dec 5, 2018
9b63b47
Misc test additions
sloretz Dec 5, 2018
a4b1cb4
Windows independent_bits_engine can't do uint8_t
sloretz Dec 5, 2018
ffa4dce
Windows link issues
sloretz Dec 5, 2018
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
477 changes: 449 additions & 28 deletions rclcpp_action/include/rclcpp_action/client.hpp

Large diffs are not rendered by default.

96 changes: 84 additions & 12 deletions rclcpp_action/include/rclcpp_action/client_goal_handle.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,39 +17,111 @@

#include <rcl_action/action_client.h>

#include <action_msgs/msg/goal_status.hpp>
#include <rclcpp/macros.hpp>
#include <rclcpp/time.hpp>

#include <functional>
#include <future>
#include <memory>
#include <mutex>

#include "rclcpp_action/types.hpp"
#include "rclcpp_action/visibility_control.hpp"

namespace rclcpp_action
{
/// The possible statuses that an action goal can finish with.
enum class ResultCode : int8_t
{
UNKNOWN = action_msgs::msg::GoalStatus::STATUS_UNKNOWN,
SUCCEEDED = action_msgs::msg::GoalStatus::STATUS_SUCCEEDED,
CANCELED = action_msgs::msg::GoalStatus::STATUS_CANCELED,
ABORTED = action_msgs::msg::GoalStatus::STATUS_ABORTED
};


// Forward declarations
template<typename ACTION>
template<typename ActionT>
class Client;

template<typename ACTION>
template<typename ActionT>
class ClientGoalHandle
{
public:
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(ClientGoalHandle)

// A wrapper that defines the result of an action
typedef struct Result
{
/// The unique identifier of the goal
GoalID goal_id;
/// A status to indicate if the goal was canceled, aborted, or suceeded
ResultCode code;
/// User defined fields sent back with an action
typename ActionT::Result::SharedPtr response;
} Result;

using Feedback = typename ActionT::Feedback;
using FeedbackCallback =
std::function<void (ClientGoalHandle::SharedPtr, const std::shared_ptr<const Feedback>)>;

virtual ~ClientGoalHandle();

/// TODO(sloretz) examples have this on client as `async_cancel_goal(goal_handle)`
std::future<bool>
async_cancel();
const GoalID &
get_goal_id() const;

rclcpp::Time
get_goal_stamp() const;

std::future<typename ACTION::Result>
std::shared_future<Result>
async_result();

int8_t
get_status();

bool
is_feedback_aware();

bool
is_result_aware();

private:
// The templated Server creates goal handles
friend Client<ACTION>;
// The templated Client creates goal handles
friend Client<ActionT>;

ClientGoalHandle(const GoalInfo & info, FeedbackCallback callback);

void
set_feedback_callback(FeedbackCallback callback);

void
call_feedback_callback(
ClientGoalHandle<ActionT>::SharedPtr shared_this,
typename std::shared_ptr<const Feedback> feedback_message);

void
set_result_awareness(bool awareness);

void
set_status(int8_t status);

void
set_result(const Result & result);

void
invalidate();

GoalInfo info_;

bool is_result_aware_{false};
std::promise<Result> result_promise_;
std::shared_future<Result> result_future_;

ClientGoalHandle(rcl_action_client_t * rcl_client, const rcl_action_goal_info_t rcl_info);
FeedbackCallback feedback_callback_{nullptr};
int8_t status_{GoalStatus::STATUS_ACCEPTED};

// TODO(sloretz) shared pointer to keep rcl_client_ alive while goal handles are alive
rcl_action_client_t * rcl_client_;
rcl_action_goal_info_t rcl_info_;
std::mutex handle_mutex_;
};
} // namespace rclcpp_action

Expand Down
135 changes: 119 additions & 16 deletions rclcpp_action/include/rclcpp_action/client_goal_handle_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,35 +17,138 @@

#include <rcl_action/types.h>

#include <memory>

#include "rclcpp_action/exceptions.hpp"

namespace rclcpp_action
{
template<typename ACTION>
ClientGoalHandle<ACTION>::ClientGoalHandle(
rcl_action_client_t * rcl_client,
const rcl_action_goal_info_t rcl_info
)
: rcl_client_(rcl_client), rcl_info_(rcl_info)

template<typename ActionT>
ClientGoalHandle<ActionT>::ClientGoalHandle(
const GoalInfo & info, FeedbackCallback callback)
: info_(info), result_future_(result_promise_.get_future()), feedback_callback_(callback)
{
}

template<typename ActionT>
ClientGoalHandle<ActionT>::~ClientGoalHandle()
{
}

template<typename ActionT>
const GoalID &
ClientGoalHandle<ActionT>::get_goal_id() const
{
// return info_.goal_id;
return info_.goal_id.uuid;
}

template<typename ActionT>
rclcpp::Time
ClientGoalHandle<ActionT>::get_goal_stamp() const
{
return info_.stamp;
}

template<typename ACTION>
ClientGoalHandle<ACTION>::~ClientGoalHandle()

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

excess space

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

template<typename ActionT>
std::shared_future<typename ClientGoalHandle<ActionT>::Result>
ClientGoalHandle<ActionT>::async_result()
{
std::lock_guard<std::mutex> guard(handle_mutex_);
if (!is_result_aware_) {
throw exceptions::UnawareGoalHandleError();
}
return result_future_;
}

template<typename ACTION>
std::future<bool>
ClientGoalHandle<ACTION>::async_cancel()
template<typename ActionT>
void
ClientGoalHandle<ActionT>::set_result(const Result & result)
{
throw std::runtime_error("Failed to cancel goal");
std::lock_guard<std::mutex> guard(handle_mutex_);
status_ = static_cast<int8_t>(result.code);
result_promise_.set_value(result);
}

template<typename ACTION>
std::future<typename ACTION::Result>
ClientGoalHandle<ACTION>::async_result()
template<typename ActionT>
void
ClientGoalHandle<ActionT>::set_feedback_callback(FeedbackCallback callback)
{
throw std::runtime_error("Failed to get result future");
std::lock_guard<std::mutex> guard(handle_mutex_);
feedback_callback_ = callback;
}

template<typename ActionT>
int8_t
ClientGoalHandle<ActionT>::get_status()
{
std::lock_guard<std::mutex> guard(handle_mutex_);
return status_;
}

template<typename ActionT>
void
ClientGoalHandle<ActionT>::set_status(int8_t status)
{
std::lock_guard<std::mutex> guard(handle_mutex_);
status_ = status;
}

template<typename ActionT>
bool
ClientGoalHandle<ActionT>::is_feedback_aware()
{
std::lock_guard<std::mutex> guard(handle_mutex_);
return feedback_callback_ != nullptr;
}

template<typename ActionT>
bool
ClientGoalHandle<ActionT>::is_result_aware()
{
std::lock_guard<std::mutex> guard(handle_mutex_);
return is_result_aware_;
}

template<typename ActionT>
void
ClientGoalHandle<ActionT>::set_result_awareness(bool awareness)
{
std::lock_guard<std::mutex> guard(handle_mutex_);
is_result_aware_ = awareness;
}

template<typename ActionT>
void
ClientGoalHandle<ActionT>::invalidate()
{
std::lock_guard<std::mutex> guard(handle_mutex_);
status_ = GoalStatus::STATUS_UNKNOWN;
result_promise_.set_exception(std::make_exception_ptr(
exceptions::UnawareGoalHandleError()));
}

template<typename ActionT>
void
ClientGoalHandle<ActionT>::call_feedback_callback(
ClientGoalHandle<ActionT>::SharedPtr shared_this,
typename std::shared_ptr<const Feedback> feedback_message)
{
if (shared_this.get() != this) {
RCLCPP_ERROR(rclcpp::get_logger("rclcpp_action"), "Sent feedback to wrong goal handle.");
return;
}
std::lock_guard<std::mutex> guard(handle_mutex_);
if (feedback_callback_ == nullptr) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (feedback_callback_ == nullptr) {
if (nullptr == feedback_callback_) {

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// Normal, some feedback messages may arrive after the goal result.
RCLCPP_DEBUG(rclcpp::get_logger("rclcpp_action"), "Received feedback but goal ignores it.");
return;
}
feedback_callback_(shared_this, feedback_message);
}

} // namespace rclcpp_action

#endif // RCLCPP_ACTION__CLIENT_GOAL_HANDLE_IMPL_HPP_
50 changes: 42 additions & 8 deletions rclcpp_action/include/rclcpp_action/create_client.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,50 @@

namespace rclcpp_action
{
template<typename ACTION>
typename Client<ACTION>::SharedPtr
template<typename ActionT>
typename Client<ActionT>::SharedPtr
create_client(
rclcpp::Node * node,
const std::string & name)
rclcpp::Node::SharedPtr node,
const std::string & name,
rclcpp::callback_group::CallbackGroup::SharedPtr group = nullptr)
{
return Client<ACTION>::make_shared(
node->get_node_base_interface(),
name);
std::weak_ptr<rclcpp::node_interfaces::NodeWaitablesInterface> weak_node =
node->get_node_waitables_interface();
std::weak_ptr<rclcpp::callback_group::CallbackGroup> weak_group = group;
bool group_is_null = (nullptr == group.get());

auto deleter = [weak_node, weak_group, group_is_null](Client<ActionT> * ptr)
{
if (nullptr == ptr) {
return;
}
auto shared_node = weak_node.lock();
if (!shared_node) {
return;
}
// API expects a shared pointer, give it one with a deleter that does nothing.
std::shared_ptr<Client<ActionT>> fake_shared_ptr(ptr, [](Client<ActionT> *) {});

if (group_is_null) {
// Was added to default group
shared_node->remove_waitable(fake_shared_ptr, nullptr);
} else {
// Was added to a specfic group
auto shared_group = weak_group.lock();
if (shared_group) {
shared_node->remove_waitable(fake_shared_ptr, shared_group);
}
}
delete ptr;
};

std::shared_ptr<Client<ActionT>> action_client(
new Client<ActionT>(node->get_node_base_interface(), node->get_node_logging_interface(), name),
deleter);

node->get_node_waitables_interface()->add_waitable(action_client, group);
return action_client;
}
} // namespace rclcpp_action

#endif // RCLCPP_ACTION__CREATE_CLIENT_HPP_
// TODO(sloretz) rclcpp_action::create_client<>(node, action_name);
46 changes: 46 additions & 0 deletions rclcpp_action/include/rclcpp_action/exceptions.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright 2018 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef RCLCPP_ACTION__EXCEPTIONS_HPP_
#define RCLCPP_ACTION__EXCEPTIONS_HPP_

#include <stdexcept>

namespace rclcpp_action
{
namespace exceptions
{
class UnknownGoalHandleError : public std::invalid_argument
{
public:
UnknownGoalHandleError()
: std::invalid_argument("Goal handle is not know to this client.")
{
}
};

class UnawareGoalHandleError : public std::runtime_error
{
public:
UnawareGoalHandleError()
: std::runtime_error("Goal handle is not tracking the goal result.")
{
}
};

} // namespace exceptions

} // namespace rclcpp_action

#endif // RCLCPP_ACTION__EXCEPTIONS_HPP_
Loading