
This fixes a runtime error caused by a race condition when making consecutive requests for the result. Specifically, this happens if the user provides a result callback when sending a goal and then calls async_get_result shortly after. Resolves #783 Signed-off-by: Jacob Perron <jacob@openrobotics.org>
165 lines
4.7 KiB
C++
165 lines
4.7 KiB
C++
// 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__CLIENT_GOAL_HANDLE_HPP_
|
|
#define RCLCPP_ACTION__CLIENT_GOAL_HANDLE_HPP_
|
|
|
|
#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 ActionT>
|
|
class Client;
|
|
|
|
/// Class for interacting with goals sent from action clients.
|
|
/**
|
|
* Use this class to check the status of a goal as well as get the result.
|
|
*
|
|
* This class is not meant to be created by a user, instead it is created when a goal has been
|
|
* accepted.
|
|
* A `Client` will create an instance and return it to the user (via a future) after calling
|
|
* `Client::async_send_goal`.
|
|
*/
|
|
template<typename ActionT>
|
|
class ClientGoalHandle
|
|
{
|
|
public:
|
|
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(ClientGoalHandle)
|
|
|
|
// A wrapper that defines the result of an action
|
|
typedef struct WrappedResult
|
|
{
|
|
/// The unique identifier of the goal
|
|
GoalUUID 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 result;
|
|
} WrappedResult;
|
|
|
|
using Feedback = typename ActionT::Feedback;
|
|
using Result = typename ActionT::Result;
|
|
using FeedbackCallback =
|
|
std::function<void (
|
|
typename ClientGoalHandle<ActionT>::SharedPtr,
|
|
const std::shared_ptr<const Feedback>)>;
|
|
using ResultCallback = std::function<void (const WrappedResult & result)>;
|
|
|
|
virtual ~ClientGoalHandle();
|
|
|
|
/// Get the unique ID for the goal.
|
|
const GoalUUID &
|
|
get_goal_id() const;
|
|
|
|
/// Get the time when the goal was accepted.
|
|
rclcpp::Time
|
|
get_goal_stamp() const;
|
|
|
|
/// Get a future to the goal result.
|
|
/**
|
|
* This method should not be called if the `ignore_result` flag was set when
|
|
* sending the original goal request (see Client::async_send_goal).
|
|
*
|
|
* `is_result_aware()` can be used to check if it is safe to call this method.
|
|
*
|
|
* \throws exceptions::UnawareGoalHandleError If the the goal handle is unaware of the result.
|
|
* \return A future to the result.
|
|
*/
|
|
std::shared_future<WrappedResult>
|
|
async_result();
|
|
|
|
/// Get the goal status code.
|
|
int8_t
|
|
get_status();
|
|
|
|
/// Check if an action client has subscribed to feedback for the goal.
|
|
bool
|
|
is_feedback_aware();
|
|
|
|
/// Check if an action client has requested the result for the goal.
|
|
bool
|
|
is_result_aware();
|
|
|
|
private:
|
|
// The templated Client creates goal handles
|
|
friend Client<ActionT>;
|
|
|
|
ClientGoalHandle(
|
|
const GoalInfo & info,
|
|
FeedbackCallback feedback_callback,
|
|
ResultCallback result_callback);
|
|
|
|
void
|
|
set_feedback_callback(FeedbackCallback callback);
|
|
|
|
void
|
|
set_result_callback(ResultCallback callback);
|
|
|
|
void
|
|
call_feedback_callback(
|
|
typename ClientGoalHandle<ActionT>::SharedPtr shared_this,
|
|
typename std::shared_ptr<const Feedback> feedback_message);
|
|
|
|
/// Returns the previous value of awareness
|
|
bool
|
|
set_result_awareness(bool awareness);
|
|
|
|
void
|
|
set_status(int8_t status);
|
|
|
|
void
|
|
set_result(const WrappedResult & wrapped_result);
|
|
|
|
void
|
|
invalidate();
|
|
|
|
GoalInfo info_;
|
|
|
|
bool is_result_aware_{false};
|
|
std::promise<WrappedResult> result_promise_;
|
|
std::shared_future<WrappedResult> result_future_;
|
|
|
|
FeedbackCallback feedback_callback_{nullptr};
|
|
ResultCallback result_callback_{nullptr};
|
|
int8_t status_{GoalStatus::STATUS_ACCEPTED};
|
|
|
|
std::mutex handle_mutex_;
|
|
};
|
|
} // namespace rclcpp_action
|
|
|
|
#include <rclcpp_action/client_goal_handle_impl.hpp> // NOLINT(build/include_order)
|
|
#endif // RCLCPP_ACTION__CLIENT_GOAL_HANDLE_HPP_
|