#include"example_interfaces/action/fibonacci.hpp" #include"rclcpp/rclcpp.hpp" // TODO(jacobperron): Remove this once it is included as part of 'rclcpp.hpp' #include"rclcpp_action/rclcpp_action.hpp"
classMinimalActionServer : public rclcpp::Node { public: // 类型重命名,可以有效减少代码长度 using Fibonacci = example_interfaces::action::Fibonacci; using GoalHandleFibonacci = rclcpp_action::ServerGoalHandle<Fibonacci>;
#include"example_interfaces/action/fibonacci.hpp" #include"rclcpp/rclcpp.hpp" // TODO(jacobperron): Remove this once it is included as part of 'rclcpp.hpp' #include"rclcpp_action/rclcpp_action.hpp"
classMinimalActionClient : public rclcpp::Node { public: using Fibonacci = example_interfaces::action::Fibonacci; using GoalHandleFibonacci = rclcpp_action::ClientGoalHandle<Fibonacci>;
if (!this->client_ptr_) { RCLCPP_ERROR(this->get_logger(), "Action client not initialized"); }
// 等待动作服务被建立 if (!this->client_ptr_->wait_for_action_server(std::chrono::seconds(10))) { RCLCPP_ERROR(this->get_logger(), "Action server not available after waiting"); this->goal_done_ = true; return; }
// 建立计算目标 auto goal_msg = Fibonacci::Goal(); goal_msg.order = 10;
voidgoal_response_callback(GoalHandleFibonacci::SharedPtr goal_handle) { if (!goal_handle) { RCLCPP_ERROR(this->get_logger(), "Goal was rejected by server"); } else { RCLCPP_INFO(this->get_logger(), "Goal accepted by server, waiting for result"); } }
voidfeedback_callback( GoalHandleFibonacci::SharedPtr, const std::shared_ptr<const Fibonacci::Feedback> feedback) { RCLCPP_INFO( this->get_logger(), "Next number in sequence received: %" PRId32, feedback->sequence.back()); }
voidresult_callback(const GoalHandleFibonacci::WrappedResult & result) { this->goal_done_ = true; switch (result.code) { case rclcpp_action::ResultCode::SUCCEEDED: break; case rclcpp_action::ResultCode::ABORTED: RCLCPP_ERROR(this->get_logger(), "Goal was aborted"); return; case rclcpp_action::ResultCode::CANCELED: RCLCPP_ERROR(this->get_logger(), "Goal was canceled"); return; default: RCLCPP_ERROR(this->get_logger(), "Unknown result code"); return; }
RCLCPP_INFO(this->get_logger(), "Result received"); for (auto number : result.result->sequence) { RCLCPP_INFO(this->get_logger(), "%" PRId32, number); } } }; // class MinimalActionClient
auto deleter = [weak_node, weak_group, group_is_null](Server<ActionT> * ptr) { if (nullptr == ptr) { return; } auto shared_node = weak_node.lock(); if (shared_node) { // API expects a shared pointer, give it one with a deleter that does nothing. std::shared_ptr<Server<ActionT>> fake_shared_ptr(ptr, [](Server<ActionT> *) {});
if (group_is_null) { // Was added to default group shared_node->remove_waitable(fake_shared_ptr, nullptr); } else { // Was added to a specific group auto shared_group = weak_group.lock(); if (shared_group) { shared_node->remove_waitable(fake_shared_ptr, shared_group); } } } delete ptr; };
template<typename ActionT> classServer : public ServerBase, public std::enable_shared_from_this<Server<ActionT>> { public: RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(Server)
/// Signature of a callback that accepts or rejects goal requests. using GoalCallback = std::function<GoalResponse( const GoalUUID &, std::shared_ptr<consttypename ActionT::Goal>)>; /// Signature of a callback that accepts or rejects requests to cancel a goal. using CancelCallback = std::function<CancelResponse(std::shared_ptr<ServerGoalHandle<ActionT>>)>; /// Signature of a callback that is used to notify when the goal has been accepted. using AcceptedCallback = std::function<void (std::shared_ptr<ServerGoalHandle<ActionT>>)>;
/// Structure which encapsulates a ROS Action Server. typedefstructrcl_action_server_s { /// Pointer to the action server implementation rcl_action_server_impl_t * impl; } rcl_action_server_t;
// Store reference to clock action_server->impl->clock = clock;
// 初始化action_server->impl->expire_timer超时定时器 ret = rcl_timer_init( &action_server->impl->expire_timer, action_server->impl->clock, node->context, options->result_timeout.nanoseconds, NULL, allocator); if (RCL_RET_OK != ret) { goto fail; } // Cancel timer so it doesn't start firing ret = rcl_timer_cancel(&action_server->impl->expire_timer); if (RCL_RET_OK != ret) { goto fail; }
// Copy action name action_server->impl->action_name = rcutils_strdup(action_name, allocator); if (NULL == action_server->impl->action_name) { ret = RCL_RET_BAD_ALLOC; goto fail; } return ret; fail: { // Finalize any services/publishers that were initialized and deallocate action_server->impl rcl_ret_t ret_throwaway = rcl_action_server_fini(action_server, node); // Since there is already a failure, it is likely that finalize will error on one or more of // the action servers members (void)ret_throwaway; } return ret; }
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) { // 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 specific group auto shared_group = weak_group.lock(); if (shared_group) { shared_node->remove_waitable(fake_shared_ptr, shared_group); } } } delete ptr; };
// src/ros2/rclcpp/rclcpp_action/include/rclcpp_action/client.hpp classClient : public ClientBase { public: RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(Client<ActionT>)
using Goal = typename ActionT::Goal; using Feedback = typename ActionT::Feedback; using GoalHandle = ClientGoalHandle<ActionT>; using WrappedResult = typename GoalHandle::WrappedResult; using GoalResponseCallback = std::function<void (typename GoalHandle::SharedPtr)>; using FeedbackCallback = typename GoalHandle::FeedbackCallback; using ResultCallback = typename GoalHandle::ResultCallback; using CancelRequest = typename ActionT::Impl::CancelGoalService::Request; using CancelResponse = typename ActionT::Impl::CancelGoalService::Response; using CancelCallback = std::function<void (typename CancelResponse::SharedPtr)>;
/// Options for sending a goal. /** * This struct is used to pass parameters to the function `async_send_goal`. */ structSendGoalOptions { SendGoalOptions() : goal_response_callback(nullptr), feedback_callback(nullptr), result_callback(nullptr) { }
/// Function called when the goal is accepted or rejected. /** * Takes a single argument that is a goal handle shared pointer. * If the goal is accepted, then the pointer points to a valid goal handle. * If the goal is rejected, then pointer has the value `nullptr`. */ GoalResponseCallback goal_response_callback;
/// Function called whenever feedback is received for the goal. FeedbackCallback feedback_callback;
/// Function called when the result for the goal is received. ResultCallback result_callback; };
// Lock for action_client_ std::recursive_mutex action_client_mutex_;
// next ready event for taking, will be set by is_ready and will be processed by take_data std::atomic<size_t> next_ready_event; // used to indicate that next_ready_event has no ready event for processing staticconstexprsize_t NO_EVENT_READY = std::numeric_limits<size_t>::max();
rclcpp::Context::SharedPtr context_; rclcpp::node_interfaces::NodeGraphInterface::WeakPtr node_graph_; // node_handle must be destroyed after client_handle to prevent memory leak std::shared_ptr<rcl_node_t> node_handle{nullptr}; std::shared_ptr<rcl_action_client_t> client_handle{nullptr}; rclcpp::Logger logger;
using ResponseCallback = std::function<void (std::shared_ptr<void> response)>;
// 调用goal的回调,此时参数有效 if (options.goal_response_callback) { options.goal_response_callback(goal_handle); }
// 向服务端发送goal result请求 if (options.result_callback) { this->make_result_aware(goal_handle); } });
// 清除不再使用goal,暂不关心 { std::lock_guard<std::mutex> guard(goal_handles_mutex_); auto goal_handle_it = goal_handles_.begin(); while (goal_handle_it != goal_handles_.end()) { if (!goal_handle_it->second.lock()) { RCLCPP_DEBUG( this->get_logger(), "Dropping weak reference to goal handle during send_goal()"); goal_handle_it = goal_handles_.erase(goal_handle_it); } else { ++goal_handle_it; } } }
return future; }
/// \internal void make_result_aware(typename GoalHandle::SharedPtr goal_handle) { // Avoid making more than one request if (goal_handle->set_result_awareness(true)) { return; } using GoalResultRequest = typename ActionT::Impl::GetResultService::Request; auto goal_result_request = std::make_shared<GoalResultRequest>(); goal_result_request->goal_id.uuid = goal_handle->get_goal_id(); try { // 这里发送结果请求 this->send_result_request( std::static_pointer_cast<void>(goal_result_request), [goal_handle, this](std::shared_ptr<void> response) mutable { // Wrap the response in a struct with the fields a user cares about WrappedResult wrapped_result; using GoalResultResponse = typename ActionT::Impl::GetResultService::Response; // 处理结果,拷贝到wrapped_result auto result_response = std::static_pointer_cast<GoalResultResponse>(response); wrapped_result.result = std::make_shared<typename ActionT::Result>(); *wrapped_result.result = result_response->result; wrapped_result.goal_id = goal_handle->get_goal_id(); wrapped_result.code = static_cast<ResultCode>(result_response->status); // 这里将结果也放到goal_handle中 goal_handle->set_result(wrapped_result); std::lock_guard<std::mutex> lock(goal_handles_mutex_); goal_handles_.erase(goal_handle->get_goal_id()); }); } catch (rclcpp::exceptions::RCLError & ex) { // This will cause an exception when the user tries to access the result goal_handle->invalidate(exceptions::UnawareGoalHandleError(ex.message)); } } };
// A wrapper that defines the result of an action structWrappedResult { /// 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; };
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();
.....// 其他代码逻辑
private: // The templated Client creates goal handles friendclassClient<ActionT>;
// src/ros2/rclcpp/rclcpp/src/rclcpp/executor.cpp void Executor::execute_any_executable(AnyExecutable & any_exec) { if (!spinning.load()) { return; } ....//其他代码逻辑 if (any_exec.waitable) { any_exec.waitable->execute(any_exec.data); } // Reset the callback_group, regardless of type any_exec.callback_group->can_be_taken_from().store(true); ....//其他代码逻辑 }
bool Executor::get_next_ready_executable_from_map( AnyExecutable & any_executable, const rclcpp::memory_strategy::MemoryStrategy::WeakCallbackGroupsToNodesMap & weak_groups_to_nodes) { TRACEPOINT(rclcpp_executor_get_next_ready); bool success = false; std::lock_guard<std::mutex> guard{mutex_}; ......//其他代码逻辑 if (!success) { // Check the waitables to see if there are any that are ready memory_strategy_->get_next_waitable(any_executable, weak_groups_to_nodes); if (any_executable.waitable) { any_executable.data = any_executable.waitable->take_data(); success = true; } } ......// 其他代码逻辑 if (success) { // If it is valid, check to see if the group is mutually exclusive or // not, then mark it accordingly ..Check if the callback_group belongs to this executor if (any_executable.callback_group && any_executable.callback_group->type() == \ CallbackGroupType::MutuallyExclusive) { // It should not have been taken otherwise assert(any_executable.callback_group->can_be_taken_from().load()); // Set to false to indicate something is being run from this group // This is reset to true either when the any_executable is executed or when the // any_executable is destructued any_executable.callback_group->can_be_taken_from().store(false); } } // If there is no ready executable, return false return success; }
if (next_ready_event == ServerBaseImpl::NO_EVENT_READY) { throw std::runtime_error("Taking data from action server but no ready event"); } // 调用take_data_by_entity_id处理数据 returntake_data_by_entity_id(next_ready_event); }
std::shared_ptr<ServerBaseData> data_ptr; // Mark as ready the entity from which we want to take data switch (static_cast<ActionEventType>(id)) { // GoalService类型 // case ActionEventType::GoalService: { rcl_ret_t ret; rcl_action_goal_info_t goal_info = rcl_action_get_zero_initialized_goal_info(); rmw_request_id_t request_header;
if (RCL_RET_ACTION_SERVER_TAKE_FAILED == ret) { // Ignore take failure because connext fails if it receives a sample without valid data. // This happens when a client shuts down and connext receives a sample saying the client is // no longer alive. return; } elseif (RCL_RET_OK != ret) { rclcpp::exceptions::throw_from_rcl_error(ret); }
if (RCL_RET_OK != ret) { if (ret == RCL_RET_TIMEOUT) { RCLCPP_WARN( pimpl_->logger_, "Failed to send goal response %s (timeout): %s", to_string(uuid).c_str(), rcl_get_error_string().str); rcl_reset_error(); return; } else { rclcpp::exceptions::throw_from_rcl_error(ret); } }
constauto status = response_pair.first;
// if goal is accepted, create a goal handle, and store it if (GoalResponse::ACCEPT_AND_EXECUTE == status || GoalResponse::ACCEPT_AND_DEFER == status) { RCLCPP_DEBUG(pimpl_->logger_, "Accepted goal %s", to_string(uuid).c_str()); // rcl_action will set time stamp auto deleter = [](rcl_action_goal_handle_t * ptr) { if (nullptr != ptr) { rcl_ret_t fail_ret = rcl_action_goal_handle_fini(ptr); if (RCL_RET_OK != fail_ret) { RCLCPP_DEBUG( rclcpp::get_logger("rclcpp_action"), "failed to fini rcl_action_goal_handle_t in deleter"); } delete ptr; } }; // 当Goal被用户接受,调用rcl创建Goal Handle rcl_action_goal_handle_t * rcl_handle; { std::lock_guard<std::recursive_mutex> lock(pimpl_->action_server_reentrant_mutex_); rcl_handle = rcl_action_accept_new_goal(pimpl_->action_server_.get(), &goal_info); } if (!rcl_handle) { throw std::runtime_error("Failed to accept new goal\n"); }
std::shared_ptr<rcl_action_goal_handle_t> handle(newrcl_action_goal_handle_t, deleter); // Copy out goal handle since action server storage disappears when it is fini'd *handle = *rcl_handle;
// 更新底层rcl,表示Goal正在执行 if (GoalResponse::ACCEPT_AND_EXECUTE == status) { // Change status to executing ret = rcl_action_update_goal_state(handle.get(), GOAL_EVENT_EXECUTE); if (RCL_RET_OK != ret) { rclcpp::exceptions::throw_from_rcl_error(ret); } } // publish status since a goal's state has changed (was accepted or has begun execution) // 将Goal状态发不出去,这个就会使用服务端建立Status Publisher,这个暂不关注 publish_status();
template<typename ActionT> classServer : public ServerBase, public std::enable_shared_from_this<Server<ActionT>> { public: RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(Server)
/// Signature of a callback that accepts or rejects goal requests. using GoalCallback = std::function<GoalResponse( const GoalUUID &, std::shared_ptr<consttypename ActionT::Goal>)>; /// Signature of a callback that accepts or rejects requests to cancel a goal. using CancelCallback = std::function<CancelResponse(std::shared_ptr<ServerGoalHandle<ActionT>>)>; /// Signature of a callback that is used to notify when the goal has been accepted. using AcceptedCallback = std::function<void (std::shared_ptr<ServerGoalHandle<ActionT>>)>;
template<typename ActionT> classServer : public ServerBase, public std::enable_shared_from_this<Server<ActionT>> { public: RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(Server)
/// Signature of a callback that accepts or rejects goal requests. using GoalCallback = std::function<GoalResponse( const GoalUUID &, std::shared_ptr<consttypename ActionT::Goal>)>; /// Signature of a callback that accepts or rejects requests to cancel a goal. using CancelCallback = std::function<CancelResponse(std::shared_ptr<ServerGoalHandle<ActionT>>)>; /// Signature of a callback that is used to notify when the goal has been accepted. using AcceptedCallback = std::function<void (std::shared_ptr<ServerGoalHandle<ActionT>>)>;
std::function<void(const GoalUUID &, std::shared_ptr<void>)> on_terminal_state = [weak_this](const GoalUUID & goal_uuid, std::shared_ptr<void> result_message) { std::shared_ptr<Server<ActionT>> shared_this = weak_this.lock(); if (!shared_this) { return; } // Send result message to anyone that asked shared_this->publish_result(goal_uuid, result_message); // Publish a status message any time a goal handle changes state shared_this->publish_status(); // notify base so it can recalculate the expired goal timer shared_this->notify_goal_terminal_state(); // Delete data now (ServerBase and rcl_action_server_t keep data until goal handle expires) std::lock_guard<std::mutex> lock(shared_this->goal_handles_mutex_); shared_this->goal_handles_.erase(goal_uuid); };
std::function<void(const GoalUUID &)> on_executing = [weak_this](const GoalUUID & goal_uuid) { std::shared_ptr<Server<ActionT>> shared_this = weak_this.lock(); if (!shared_this) { return; } (void)goal_uuid; // Publish a status message any time a goal handle changes state shared_this->publish_status(); };
// Mark as ready the entity from which we want to take data switch (static_cast<EntityType>(id)) { // Goal响应 case EntityType::GoalClient: { rmw_request_id_t response_header; std::shared_ptr<void> goal_response; { std::lock_guard<std::recursive_mutex> lock(pimpl_->action_client_mutex_);
// src/ros2/rclcpp/rclcpp_action/include/rclcpp_action/client.hpp template<typename ActionT> classClient : public ClientBase { void make_result_aware(typename GoalHandle::SharedPtr goal_handle) { void ClientBase::send_result_request(std::shared_ptr<void> request, ResponseCallback callback) { std::lock_guard<std::mutex> guard(pimpl_->result_requests_mutex); int64_t sequence_number; rcl_ret_t ret = rcl_action_send_result_request( pimpl_->client_handle.get(), request.get(), &sequence_number); if (RCL_RET_OK != ret) { rclcpp::exceptions::throw_from_rcl_error(ret, "failed to send result request"); } assert(pimpl_->pending_result_responses.count(sequence_number) == 0); pimpl_->pending_result_responses[sequence_number] = callback; }
// Avoid making more than one request if (goal_handle->set_result_awareness(true)) { return; } using GoalResultRequest = typename ActionT::Impl::GetResultService::Request; auto goal_result_request = std::make_shared<GoalResultRequest>(); goal_result_request->goal_id.uuid = goal_handle->get_goal_id(); try { // 异步发送Result请求 this->send_result_request( std::static_pointer_cast<void>(goal_result_request), [goal_handle, this](std::shared_ptr<void> response) mutable { // Wrap the response in a struct with the fields a user cares about WrappedResult wrapped_result; using GoalResultResponse = typename ActionT::Impl::GetResultService::Response; auto result_response = std::static_pointer_cast<GoalResultResponse>(response); wrapped_result.result = std::make_shared<typename ActionT::Result>(); *wrapped_result.result = result_response->result; wrapped_result.goal_id = goal_handle->get_goal_id(); wrapped_result.code = static_cast<ResultCode>(result_response->status); goal_handle->set_result(wrapped_result); std::lock_guard<std::mutex> lock(goal_handles_mutex_); goal_handles_.erase(goal_handle->get_goal_id()); }); } catch (rclcpp::exceptions::RCLError & ex) { // This will cause an exception when the user tries to access the result goal_handle->invalidate(exceptions::UnawareGoalHandleError(ex.message)); } } };
std::shared_ptr<void> ServerBase::take_data_by_entity_id(size_t id) { .......//其他代码逻辑 std::shared_ptr<ServerBaseData> data_ptr; // Mark as ready the entity from which we want to take data switch (static_cast<ActionEventType>(id)) { .......//其他代码逻辑 case ActionEventType::ResultService: { rcl_ret_t ret; // Get the result request message rmw_request_id_t request_header; std::shared_ptr<void> result_request = create_result_request(); std::lock_guard<std::recursive_mutex> lock(pimpl_->action_server_reentrant_mutex_); ret = rcl_action_take_result_request( pimpl_->action_server_.get(), &request_header, result_request.get());
if (RCL_RET_ACTION_SERVER_TAKE_FAILED == ret) { // Ignore take failure because connext fails if it receives a sample without valid data. // This happens when a client shuts down and connext receives a sample saying the client is // no longer alive. return; } elseif (RCL_RET_OK != ret) { rclcpp::exceptions::throw_from_rcl_error(ret); }
std::function<void(const GoalUUID &, std::shared_ptr<void>)> on_terminal_state = [weak_this](const GoalUUID & goal_uuid, std::shared_ptr<void> result_message) { std::shared_ptr<Server<ActionT>> shared_this = weak_this.lock(); if (!shared_this) { return; } // Send result message to anyone that asked shared_this->publish_result(goal_uuid, result_message); // Publish a status message any time a goal handle changes state shared_this->publish_status(); // notify base so it can recalculate the expired goal timer shared_this->notify_goal_terminal_state(); // Delete data now (ServerBase and rcl_action_server_t keep data until goal handle expires) std::lock_guard<std::mutex> lock(shared_this->goal_handles_mutex_); shared_this->goal_handles_.erase(goal_uuid); }; ......// 其他代码逻辑 } };
// if there are clients who already asked for the result, send it to them auto iter = pimpl_->result_requests_.find(uuid); if (iter != pimpl_->result_requests_.end()) { std::lock_guard<std::recursive_mutex> lock(pimpl_->action_server_reentrant_mutex_); for (auto & request_header : iter->second) {
// 给客户端发送结果响应 rcl_ret_t ret = rcl_action_send_result_response( pimpl_->action_server_.get(), &request_header, result_msg.get()); if (ret == RCL_RET_TIMEOUT) { RCLCPP_WARN( pimpl_->logger_, "Failed to send result response %s (timeout): %s", to_string(uuid).c_str(), rcl_get_error_string().str); rcl_reset_error(); } elseif (RCL_RET_OK != ret) { rclcpp::exceptions::throw_from_rcl_error(ret); } } } } }
if (RCL_RET_ACTION_SERVER_TAKE_FAILED == ret) { // Ignore take failure because connext fails if it receives a sample without valid data. // This happens when a client shuts down and connext receives a sample saying the client is // no longer alive. return; } elseif (RCL_RET_OK != ret) { rclcpp::exceptions::throw_from_rcl_error(ret); }
// Convert c++ message to C message rcl_action_cancel_request_t cancel_request = rcl_action_get_zero_initialized_cancel_request(); convert(request->goal_info.goal_id.uuid, &cancel_request.goal_info); cancel_request.goal_info.stamp.sec = request->goal_info.stamp.sec; cancel_request.goal_info.stamp.nanosec = request->goal_info.stamp.nanosec;
// Get a list of goal info that should be attempted to be cancelled rcl_action_cancel_response_t cancel_response = rcl_action_get_zero_initialized_cancel_response();
if (RCL_RET_OK != ret) { rclcpp::exceptions::throw_from_rcl_error(ret); } // 这段代码在退出的时候执行,销毁cancel_response RCPPUTILS_SCOPE_EXIT( { ret = rcl_action_cancel_response_fini(&cancel_response); if (RCL_RET_OK != ret) { RCLCPP_ERROR(pimpl_->logger_, "Failed to fini cancel response"); } });
auto response = std::make_shared<action_msgs::srv::CancelGoal::Response>();
response->return_code = cancel_response.msg.return_code; auto & goals = cancel_response.msg.goals_canceling; // 对于每一个Goal,调用用户自定义的取消函数:MinimalActionServer::handle_cancel // 这里有个疑问,会有多个Goal同时存在? for (size_t i = 0; i < goals.size; ++i) { constrcl_action_goal_info_t & goal_info = goals.data[i]; GoalUUID uuid; convert(goal_info, &uuid); auto response_code = call_handle_cancel_callback(uuid); if (CancelResponse::ACCEPT == response_code) { action_msgs::msg::GoalInfo cpp_info; cpp_info.goal_id.uuid = uuid; cpp_info.stamp.sec = goal_info.stamp.sec; cpp_info.stamp.nanosec = goal_info.stamp.nanosec; response->goals_canceling.push_back(cpp_info); } }
// If the user rejects all individual requests to cancel goals, // then we consider the top-level cancel request as rejected. if (goals.size >= 1u && 0u == response->goals_canceling.size()) { response->return_code = action_msgs::srv::CancelGoal::Response::ERROR_REJECTED; }
if (!response->goals_canceling.empty()) { // 有Goal状态改变了,这里通知变更 publish_status(); }