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 32 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
374 changes: 355 additions & 19 deletions rclcpp_action/include/rclcpp_action/client.hpp

Large diffs are not rendered by default.

82 changes: 73 additions & 9 deletions rclcpp_action/include/rclcpp_action/client_goal_handle.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,29 @@

#include <rcl_action/action_client.h>

#include <action_msgs/msg/goal_status.hpp>
#include <rclcpp/macros.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>
class Client;
Expand All @@ -32,24 +48,72 @@ template<typename ACTION>
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 ACTION::Result::SharedPtr response;
} Result;

using Feedback = typename ACTION::Feedback;
using FeedbackCallback = std::function<void (ClientGoalHandle::SharedPtr, const Feedback &)>;
sloretz marked this conversation as resolved.
Show resolved Hide resolved

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;

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

FeedbackCallback
get_feedback_callback();

int8_t
get_status();

bool
is_feedback_aware();

bool
is_result_aware();

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

ClientGoalHandle(rcl_action_client_t * rcl_client, const rcl_action_goal_info_t rcl_info);
ClientGoalHandle(const GoalInfo & info, FeedbackCallback callback);

void
set_feedback_callback(FeedbackCallback callback);

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_;

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
100 changes: 91 additions & 9 deletions rclcpp_action/include/rclcpp_action/client_goal_handle_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,15 @@

#include <rcl_action/types.h>

#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)
const GoalInfo & info, FeedbackCallback callback)
: info_(info), result_future_(result_promise_.get_future()), feedback_callback_(callback)
{
}

Expand All @@ -34,18 +35,99 @@ ClientGoalHandle<ACTION>::~ClientGoalHandle()
}

template<typename ACTION>
std::future<bool>
ClientGoalHandle<ACTION>::async_cancel()
const GoalID &
ClientGoalHandle<ACTION>::get_goal_id() const
{
throw std::runtime_error("Failed to cancel goal");
// return info_.goal_id;
return info_.goal_id.uuid;
}

template<typename ACTION>
std::future<typename ACTION::Result>
std::shared_future<typename ClientGoalHandle<ACTION>::Result>
ClientGoalHandle<ACTION>::async_result()
{
throw std::runtime_error("Failed to get result future");
std::lock_guard<std::mutex> guard(handle_mutex_);
if (!is_result_aware_) {
throw exceptions::UnawareGoalHandleError();
}
return result_future_;
}

template<typename ACTION>
void
ClientGoalHandle<ACTION>::set_result(const Result & result)
{
std::lock_guard<std::mutex> guard(handle_mutex_);
status_ = static_cast<int8_t>(result.code);
result_promise_.set_value(result);
}

template<typename ACTION>
typename ClientGoalHandle<ACTION>::FeedbackCallback
ClientGoalHandle<ACTION>::get_feedback_callback()
{
std::lock_guard<std::mutex> guard(handle_mutex_);
return feedback_callback_;
}

template<typename ACTION>
void
ClientGoalHandle<ACTION>::set_feedback_callback(FeedbackCallback callback)
{
std::lock_guard<std::mutex> guard(handle_mutex_);
feedback_callback_ = callback;
}

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

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

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

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

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

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

} // namespace rclcpp_action

#endif // RCLCPP_ACTION__CLIENT_GOAL_HANDLE_IMPL_HPP_
45 changes: 39 additions & 6 deletions rclcpp_action/include/rclcpp_action/create_client.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,46 @@ namespace rclcpp_action
template<typename ACTION>
typename Client<ACTION>::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<ACTION> * 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<ACTION>> fake_shared_ptr(ptr, [](Client<ACTION> *) {});

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<ACTION>> action_client(
new Client<ACTION>(node->get_node_base_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);
56 changes: 56 additions & 0 deletions rclcpp_action/include/rclcpp_action/exceptions.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// 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 RejectedGoalError : public std::runtime_error
{
public:
RejectedGoalError()
: std::runtime_error("")
{
}
};

class UnknownGoalHandleError : public std::invalid_argument
{
public:
UnknownGoalHandleError()
: std::invalid_argument("")
{
}
};

class UnawareGoalHandleError : public std::runtime_error
{
public:
UnawareGoalHandleError()
: std::runtime_error("")
{
}
};

} // namespace exceptions

} // namespace rclcpp_action

#endif // RCLCPP_ACTION__EXCEPTIONS_HPP_
49 changes: 49 additions & 0 deletions rclcpp_action/include/rclcpp_action/types.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// 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__TYPES_HPP_
#define RCLCPP_ACTION__TYPES_HPP_

#include <rcl_action/types.h>

#include <action_msgs/msg/goal_status.hpp>
#include <action_msgs/msg/goal_info.hpp>

#include <functional>


namespace rclcpp_action
{
// using GoalID = unique_identifier_msgs::msg::UUID;
using GoalID = std::array<uint8_t, 16>;
using GoalStatus = action_msgs::msg::GoalStatus;
using GoalInfo = action_msgs::msg::GoalInfo;

} // namespace rclcpp_action

namespace std
{
template<>
struct less<rclcpp_action::GoalID>
{
bool operator()(
const rclcpp_action::GoalID & id0,
const rclcpp_action::GoalID & id1) const
{
// return (id0.uuid < id1.uuid);
return id0 < id1;
}
};
} // namespace std
#endif // RCLCPP_ACTION__TYPES_HPP_
Loading